Better Auth rejects requests on hostnames not in hardcoded trustedOrigins (signup/signin broken on non-canonical hosts)
Opened by keeb · 4/27/2026· Shipped 4/27/2026
Summary
After PR #470 (multi-hostname feature), Better Auth rejects every state-changing request with ERR: Invalid origin when the inbound Origin header is anything other than the four hardcoded entries:
https://swamp.clubhttps://swamp-club.comhttp://localhost:8000http://127.0.0.1:8000
This breaks signup, signin, password reset, and email verification on any deployment running on a different host:
- UAT (Playwright suite in
web-deploy.yml): the harness picks a free port viafindFreePort()and runs the app at e.g.http://localhost:40669. Every signup-touching test fails. Withworkers: 1andretries: 2, all 29 specs hit the failure path × 3 attempts × ~40s of compose-up/teardown per cycle ≈ ~58 min worst case (the "Web: Deploy" step appears stuck in a loop). Confirmed locally — capturedERR: Invalid originin the Playwrighterror-context.mdfromtests/club/flows/email_signup.spec.ts. - Any preview/staging/tunnel deployment that doesn't set
TRUSTED_ORIGINS.
Steps to reproduce
- Check out
mainat 6042191 (or any commit after #470). cd swamp-uat && SWAMP_CLUB_SOURCE_DIR=/path/to/swamp-club CI=true deno run -A npm:playwright test --project=club --workers=1 tests/club/flows/email_signup.spec.ts --reporter=line- Test fails:
expect(page).toHaveURL(/\/check-email/)— page stays on/signup. - Inspect
test-results/.../error-context.md— DOM contains<alert>ERR: Invalid origin</alert>.
Root cause
lib/auth.ts:48-56 hardcodes the trustedOrigins list. The previous version included ...(authURL ? [authURL] : []) to add BETTER_AUTH_URL to the allowlist; that line was dropped in 0f8842c when the multi-hostname code landed.
Suggested fix (one line)
const authURL = Deno.env.get("BETTER_AUTH_URL");
const trustedOrigins = Array.from(
new Set([
"https://swamp.club",
"https://swamp-club.com",
"http://localhost:8000",
"http://127.0.0.1:8000",
...(authURL ? [authURL] : []),
...extraTrustedOrigins,
]),
);UAT compose already sets BETTER_AUTH_URL: http://localhost:${HOST_PORT} (the random port), so this restores the previous behavior without affecting production multi-hostname routing.
Environment
- swamp-club: 6042191 (main)
- swamp-uat: 1096f53 (main)
- Node/Deno: pinned per workflow (deno 2.7.11)
- Playwright: 1.59.1 / 1.58.2
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.