Skip to content

Harden host handling, GitHub email linking, and password reset - #1099

Merged
kody-bot merged 6 commits into
mainfrom
cursor/security-host-and-oauth-fixes-14fa
Oct 7, 2026
Merged

kody-bot merged 6 commits into
mainfrom
cursor/security-host-and-oauth-fixes-14fa

Conversation

@kentcdodds

Copy link
Copy Markdown
Member

Security hardening for auth-related flows.

  • Trusted hosts for generated URLs. getDomainUrl (used for emailed verification links, passkey config, and the image optimizer's allowed origins) now uses the request's Host/X-Forwarded-Host only if it is in the new optional ALLOWED_HOSTS env var, is ${FLY_APP_NAME}.fly.dev, or is localhost. Otherwise it uses the canonical host (first ALLOWED_HOSTS entry, then the Fly hostname, then localhost:${PORT}). The new variable is documented in env.server.ts, .env.example, docs/deployment.md, docs/security.md, and decision 048-allowed-hosts.md.
  • GitHub auth requires a verified primary email. Sign-in is refused when the GitHub primary email isn't verified, because that email is used to link to existing accounts.
  • Stricter rate limiting for /forgot-password. It's added to strongPaths.
  • Password reset signs out existing sessions.
  • Theme switch uses safeRedirect for its redirectTo.
  • Adds SECURITY.md pointing to GitHub private vulnerability reporting.

Downstream apps

Apps generated from the Epic Stack won't get these changes automatically. We recommend:

  1. Port the getDomainUrl change in app/utils/misc.tsx. If you serve a custom domain, set ALLOWED_HOSTS (e.g. fly secrets set ALLOWED_HOSTS=example.com,www.example.com). If you don't, emailed links fall back to <app>.fly.dev and passkeys won't work on the custom domain.
  2. In app/utils/providers/github.server.ts, use emails.find((e) => e.primary && e.verified).
  3. Add /forgot-password to strongPaths in server/index.ts.
  4. Optionally port the password-reset session cleanup (app/utils/auth.server.ts) and the theme switch safeRedirect (app/routes/resources/theme-switch.tsx).

Test Plan

  • New tests:
    • forgot-password.test.ts: emailed links use only trusted hosts.
    • callback.test.ts: an unverified GitHub primary email doesn't sign in or link an existing user.
    • reset-password.test.ts: sessions are deleted on reset.
    • theme-switch.test.ts: cross-origin redirectTo is rejected.
  • Confirmed each new test fails without its fix and passes with it.
  • Checked manually against a production build (start:mocks): emailed links ignore an untrusted forwarded host.
  • npm run lint, npm run typecheck, and vitest run all pass.

Checklist

  • Tests updated
  • Docs updated
Open in Web Open in Cursor 

cursoragent and others added 6 commits October 7, 2026 13:33
Add an optional ALLOWED_HOSTS env var. The request host is used only if it's
in ALLOWED_HOSTS, is the Fly.io app hostname, or is localhost; otherwise the
canonical host is used.

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@kody-bot
kody-bot merged commit 29d2830 into main Oct 7, 2026
7 checks passed
@kentcdodds
kentcdodds deleted the cursor/security-host-and-oauth-fixes-14fa branch October 7, 2026 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants