fix(GRO-2586): enforce trusted-origins allowlist on Better Auth CORS responses #219

Merged
Flea Flicker merged 1 commits from feature/GRO-2586-cors-origin-reflection into dev 2026-06-26 13:36:00 +00:00
Member

Summary

Fixes a HIGH-severity pre-existing CORS misconfiguration surfaced in GRO-2585.

Better Auth reflects the request Origin header into Access-Control-Allow-Origin unconditionally, bypassing the trustedOrigins configuration. With Access-Control-Allow-Credentials: true also set, any attacker-controlled page could XHR /api/auth/sign-in/social and read the OIDC authorize URL + state from the response body — enabling phishing flows or CSRF login priming.

Changes

  • src/lib/auth-cors.ts (new): enforceAuthCors(requestOrigin, trustedOrigins, res) — wraps a Better Auth response, stripping Access-Control-Allow-Origin/Access-Control-Allow-Credentials for untrusted origins and enforcing the correct headers for trusted ones.
  • src/index.ts: Auth handler now calls enforceAuthCors() before returning Better Auth's response, enforcing the allowlist regardless of Better Auth's internal CORS behaviour.
  • src/__tests__/authCors.test.ts (new): 6 regression tests — trusted origin passes, attacker origin stripped, undefined origin stripped, non-CORS headers preserved, second trusted origin allowed, empty-string origin treated as untrusted.
  • UAT_PLAYBOOK.md §4.1: Added TC-API-1.29/1.30/1.31 CORS test cases.

Acceptance criteria met

  • Cross-origin POST from https://evil.example.com to /api/auth/sign-in/social returns no Access-Control-Allow-Origin header.
  • Cross-origin POST from https://uat.groombook.dev returns Access-Control-Allow-Origin: https://uat.groombook.dev + Access-Control-Allow-Credentials: true.
  • All 687 existing tests continue to pass (50 test files).
  • 6 new regression tests in authCors.test.ts all pass.

Updated UAT_PLAYBOOK.md §4.1 — added TC-API-1.29, TC-API-1.30, TC-API-1.31 for CORS origin enforcement.

Related: GRO-2586 | GRO-2585

## Summary Fixes a HIGH-severity pre-existing CORS misconfiguration surfaced in [GRO-2585](/GRO/issues/GRO-2585). Better Auth reflects the request `Origin` header into `Access-Control-Allow-Origin` unconditionally, bypassing the `trustedOrigins` configuration. With `Access-Control-Allow-Credentials: true` also set, any attacker-controlled page could XHR `/api/auth/sign-in/social` and read the OIDC authorize URL + state from the response body — enabling phishing flows or CSRF login priming. ## Changes - **`src/lib/auth-cors.ts`** (new): `enforceAuthCors(requestOrigin, trustedOrigins, res)` — wraps a Better Auth response, stripping `Access-Control-Allow-Origin`/`Access-Control-Allow-Credentials` for untrusted origins and enforcing the correct headers for trusted ones. - **`src/index.ts`**: Auth handler now calls `enforceAuthCors()` before returning Better Auth's response, enforcing the allowlist regardless of Better Auth's internal CORS behaviour. - **`src/__tests__/authCors.test.ts`** (new): 6 regression tests — trusted origin passes, attacker origin stripped, `undefined` origin stripped, non-CORS headers preserved, second trusted origin allowed, empty-string origin treated as untrusted. - **`UAT_PLAYBOOK.md`** §4.1: Added TC-API-1.29/1.30/1.31 CORS test cases. ## Acceptance criteria met - Cross-origin POST from `https://evil.example.com` to `/api/auth/sign-in/social` returns **no** `Access-Control-Allow-Origin` header. - Cross-origin POST from `https://uat.groombook.dev` returns `Access-Control-Allow-Origin: https://uat.groombook.dev` + `Access-Control-Allow-Credentials: true`. - All 687 existing tests continue to pass (50 test files). - 6 new regression tests in `authCors.test.ts` all pass. Updated UAT_PLAYBOOK.md §4.1 — added TC-API-1.29, TC-API-1.30, TC-API-1.31 for CORS origin enforcement. Related: [GRO-2586](/GRO/issues/GRO-2586) | [GRO-2585](/GRO/issues/GRO-2585)
Flea Flicker added 1 commit 2026-06-26 13:30:30 +00:00
fix(GRO-2586): enforce trusted-origins allowlist on Better Auth CORS responses
CI / Test (pull_request) Successful in 28s
CI / Lint & Typecheck (pull_request) Successful in 33s
CI / Build & Push Docker Images (pull_request) Successful in 1m29s
20a0c7eb92
Better Auth reflects the request Origin into Access-Control-Allow-Origin
unconditionally, bypassing the trustedOrigins config. An attacker-origin
page could XHR /api/auth/sign-in/social with credentials and read the OIDC
authorize URL + state from the response body.

- Add src/lib/auth-cors.ts: enforceAuthCors() wraps any Better Auth Response,
  stripping ACAO/ACAC for untrusted origins and enforcing the allowlist for
  trusted ones
- Wire enforceAuthCors() into the /api/auth/* handler in src/index.ts
- Add src/__tests__/authCors.test.ts: 6 regression tests covering trusted,
  untrusted, undefined, and empty-string origins
- Update UAT_PLAYBOOK.md §4.1 with TC-API-1.29/1.30/1.31 CORS test cases

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Flea Flicker merged commit dace2c4e66 into dev 2026-06-26 13:36:00 +00:00
Sign in to join this conversation.