Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/http-transport/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,13 +438,17 @@ export class HttpTransportServer implements TransportWorker<
this.#corsOptions.origin === true
? { ...DEFAULT_CORS_PARAMS }
: { ...EXPLICIT_ORIGIN_CORS_PARAMS }
// Iterating base params also drops allowCredentials for origin: true
for (const key in params) {
const value = this.#corsOptions[key]
if (value !== undefined) {
params[key] = value
}
}
// This explicit opt-in restores credentialed origin reflection without
// weakening the safe `cors: true` default.
if (this.#corsOptions.allowCredentials !== undefined) {
params.allowCredentials = this.#corsOptions.allowCredentials
}
}
} else if (typeof this.#corsOptions === 'function') {
const result = this.#corsOptions(origin, request)
Expand All @@ -466,6 +470,9 @@ export class HttpTransportServer implements TransportWorker<
params[key] = value
}
}
if (result.allowCredentials !== undefined) {
params.allowCredentials = result.allowCredentials
}
}
}
}
Expand Down
30 changes: 11 additions & 19 deletions packages/http-transport/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,23 @@ export type HttpTransportOptions<
}

export type HttpTransportCorsCustomParams = {
/**
* `true` reflects any request origin, an array is an explicit allowlist.
* Credentials default on only for allowlisted origins.
*/
origin: true | string[]
allowMethods?: string[]
allowHeaders?: string[]
/**
* Explicitly enables credentials when reflecting request origins. Only use
* this when the API should accept credentialed requests from any website.
*/
allowCredentials?: string
maxAge?: string
exposeHeaders?: string[]
requestHeaders?: string[]
requestMethod?: string
} & (
| {
/**
* `true` reflects any request origin, an array is an explicit
* allowlist. Credentials default on for allowlisted origins only.
*/
origin: true | string[]
allowCredentials?: never
}
| {
/**
* Explicit `allowCredentials` requires an origin allowlist: combining
* credentials with a reflected origin (`origin: true`) would let any
* website make credentialed (cookie-authed) requests.
*/
origin: string[]
allowCredentials?: string
}
)
}

export type HttpTransportCorsOptions =
| true
Expand Down
19 changes: 14 additions & 5 deletions packages/http-transport/tests/cors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,23 @@ describe('CORS credentials defaults', () => {
expect(response.headers.get(ALLOW_CREDENTIALS)).toBeNull()
})

it('omits credentials for custom params with origin: true', async () => {
it('omits credentials by default for custom params with origin: true', async () => {
const response = await preflight({ origin: true })

expect(response.headers.get(ALLOW_ORIGIN)).toBe(ORIGIN)
expect(response.headers.get(ALLOW_CREDENTIALS)).toBeNull()
})

it('allows explicit credentials with reflected origins', async () => {
const response = await preflight({
origin: true,
allowCredentials: 'true',
})

expect(response.headers.get(ALLOW_ORIGIN)).toBe(ORIGIN)
expect(response.headers.get(ALLOW_CREDENTIALS)).toBe('true')
})

it('enables credentials for custom params with explicit origins', async () => {
const response = await preflight({ origin: [ORIGIN] })

Expand Down Expand Up @@ -139,7 +149,7 @@ describe('CORS Vary header', () => {
})

describe('CORS config type surface', () => {
it('accepts safe configs and rejects credentials with reflected origin', () => {
it('accepts serializable explicit credentials with reflected origins', () => {
const allowAll = Math.random() >= 0
const allowedOrigins = [ORIGIN]

Expand All @@ -151,14 +161,13 @@ describe('CORS config type surface', () => {
origin: allowedOrigins,
allowCredentials: 'true',
}
// @ts-expect-error credentials cannot be combined with origin reflection
const unsafe: HttpTransportCorsOptions = {
const reflectedCredentials: HttpTransportCorsOptions = {
origin: true,
allowCredentials: 'true',
}

expect(composed).toBeDefined()
expect(credentialed).toBeDefined()
expect(unsafe).toBeDefined()
expect(reflectedCredentials).toBeDefined()
})
})
Loading