Skip to content

Commit 5bfbff8

Browse files
jobrkJosh Brooks
andauthored
feat(cors): Allow async functions for origin and allowMethods (#4373)
* feat(cors): Allow to pass origin & methods asynchronously * fix api4 -> api8 cors test --------- Co-authored-by: Josh Brooks <[email protected]>
1 parent a268569 commit 5bfbff8

File tree

2 files changed

+79
-6
lines changed

2 files changed

+79
-6
lines changed

src/middleware/cors/index.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,32 @@ describe('CORS by Middleware', () => {
5858
})
5959
)
6060

61+
app.use(
62+
'/api8/*',
63+
cors({
64+
origin: (origin) =>
65+
new Promise<string>((resolve) =>
66+
resolve(origin.endsWith('.example.com') ? origin : 'http://example.com')
67+
),
68+
})
69+
)
70+
71+
app.use(
72+
'/api9/*',
73+
cors({
74+
origin: (origin) =>
75+
new Promise<string>((resolve) => resolve(origin === 'http://example.com' ? origin : '*')),
76+
allowMethods: (origin) =>
77+
new Promise<string[]>((resolve) =>
78+
resolve(
79+
origin === 'http://example.com'
80+
? ['GET', 'HEAD', 'POST', 'PATCH', 'DELETE']
81+
: ['GET', 'HEAD']
82+
)
83+
),
84+
})
85+
)
86+
6187
app.get('/api/abc', (c) => {
6288
return c.json({ success: true })
6389
})
@@ -202,6 +228,28 @@ describe('CORS by Middleware', () => {
202228
expect(res.headers.get('Access-Control-Allow-Origin')).toBe('http://example.com')
203229
})
204230

231+
it('Allow origins by promise returning function', async () => {
232+
let req = new Request('http://localhost/api8/abc', {
233+
headers: {
234+
Origin: 'http://subdomain.example.com',
235+
},
236+
})
237+
let res = await app.request(req)
238+
expect(res.headers.get('Access-Control-Allow-Origin')).toBe('http://subdomain.example.com')
239+
240+
req = new Request('http://localhost/api8/abc')
241+
res = await app.request(req)
242+
expect(res.headers.get('Access-Control-Allow-Origin')).toBe('http://example.com')
243+
244+
req = new Request('http://localhost/api8/abc', {
245+
headers: {
246+
Referer: 'http://evil-example.com/',
247+
},
248+
})
249+
res = await app.request(req)
250+
expect(res.headers.get('Access-Control-Allow-Origin')).toBe('http://example.com')
251+
})
252+
205253
it('With raw Response object', async () => {
206254
const res = await app.request('http://localhost/api5/abc')
207255

@@ -240,4 +288,26 @@ describe('CORS by Middleware', () => {
240288
expect(res2.headers.get('Access-Control-Allow-Origin')).toBe('*')
241289
expect(res2.headers.get('Access-Control-Allow-Methods')).toBe('GET,HEAD')
242290
})
291+
292+
it('Allow methods by promise returning function', async () => {
293+
const req = new Request('http://localhost/api9/abc', {
294+
headers: {
295+
Origin: 'http://example.com',
296+
},
297+
method: 'OPTIONS',
298+
})
299+
const res = await app.request(req)
300+
expect(res.headers.get('Access-Control-Allow-Origin')).toBe('http://example.com')
301+
expect(res.headers.get('Access-Control-Allow-Methods')).toBe('GET,HEAD,POST,PATCH,DELETE')
302+
303+
const req2 = new Request('http://localhost/api9/abc', {
304+
headers: {
305+
Origin: 'http://example.org',
306+
},
307+
method: 'OPTIONS',
308+
})
309+
const res2 = await app.request(req2)
310+
expect(res2.headers.get('Access-Control-Allow-Origin')).toBe('*')
311+
expect(res2.headers.get('Access-Control-Allow-Methods')).toBe('GET,HEAD')
312+
})
243313
})

src/middleware/cors/index.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ import type { Context } from '../../context'
77
import type { MiddlewareHandler } from '../../types'
88

99
type CORSOptions = {
10-
origin: string | string[] | ((origin: string, c: Context) => string | undefined | null)
11-
allowMethods?: string[] | ((origin: string, c: Context) => string[])
10+
origin:
11+
| string
12+
| string[]
13+
| ((origin: string, c: Context) => Promise<string> | string | undefined | null)
14+
allowMethods?: string[] | ((origin: string, c: Context) => Promise<string[]> | string[])
1215
allowHeaders?: string[]
1316
maxAge?: number
1417
credentials?: boolean
@@ -21,8 +24,8 @@ type CORSOptions = {
2124
* @see {@link https://hono.dev/docs/middleware/builtin/cors}
2225
*
2326
* @param {CORSOptions} [options] - The options for the CORS middleware.
24-
* @param {string | string[] | ((origin: string, c: Context) => string | undefined | null)} [options.origin='*'] - The value of "Access-Control-Allow-Origin" CORS header.
25-
* @param {string[] | ((origin: string, c: Context) => string[])} [options.allowMethods=['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH']] - The value of "Access-Control-Allow-Methods" CORS header.
27+
* @param {string | string[] | ((origin: string, c: Context) => Promise<string> | string | undefined | null)} [options.origin='*'] - The value of "Access-Control-Allow-Origin" CORS header.
28+
* @param {string[] | ((origin: string, c: Context) => Promise<string[]> | string[])} [options.allowMethods=['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH']] - The value of "Access-Control-Allow-Methods" CORS header.
2629
* @param {string[]} [options.allowHeaders=[]] - The value of "Access-Control-Allow-Headers" CORS header.
2730
* @param {number} [options.maxAge] - The value of "Access-Control-Max-Age" CORS header.
2831
* @param {boolean} [options.credentials] - The value of "Access-Control-Allow-Credentials" CORS header.
@@ -95,7 +98,7 @@ export const cors = (options?: CORSOptions): MiddlewareHandler => {
9598
c.res.headers.set(key, value)
9699
}
97100

98-
const allowOrigin = findAllowOrigin(c.req.header('origin') || '', c)
101+
const allowOrigin = await findAllowOrigin(c.req.header('origin') || '', c)
99102
if (allowOrigin) {
100103
set('Access-Control-Allow-Origin', allowOrigin)
101104
}
@@ -125,7 +128,7 @@ export const cors = (options?: CORSOptions): MiddlewareHandler => {
125128
set('Access-Control-Max-Age', opts.maxAge.toString())
126129
}
127130

128-
const allowMethods = findAllowMethods(c.req.header('origin') || '', c)
131+
const allowMethods = await findAllowMethods(c.req.header('origin') || '', c)
129132
if (allowMethods.length) {
130133
set('Access-Control-Allow-Methods', allowMethods.join(','))
131134
}

0 commit comments

Comments
 (0)