Skip to content

fix(login): drop Accept header — /auth/exchange POST blocked by CORS preflight (P0 follow-up)#151

Merged
mastermanas805 merged 1 commit into
mainfrom
fix/login-callback-no-accept-header
May 30, 2026
Merged

fix(login): drop Accept header — /auth/exchange POST blocked by CORS preflight (P0 follow-up)#151
mastermanas805 merged 1 commit into
mainfrom
fix/login-callback-no-accept-header

Conversation

@mastermanas805
Copy link
Copy Markdown
Member

Summary

P0 follow-up to #150. That PR added the AUTH-004 cookie-exchange flow but the fetch call included Accept: application/json, which forced a CORS preflight that the api's PreflightAllowlist rejects. Live login was still broken after #150 shipped.

Root cause

Live verification via chrome devtools MCP:

POST https://api.instanode.dev/auth/exchange   net::ERR_FAILED

Confirmed with direct curl:

$ curl -i -X OPTIONS https://api.instanode.dev/auth/exchange \
    -H 'Origin: https://instanode.dev' \
    -H 'Access-Control-Request-Method: POST' \
    -H 'Access-Control-Request-Headers: accept'
HTTP/2 403 Forbidden

api's allowlist (api/internal/router/router.go:242):

corsAllowHeaders = "Content-Type,Authorization,X-Request-ID,X-E2E-Test-Token,X-E2E-Source-IP"

Accept isn't in it → preflight 403 → browser blocks the POST with the generic "Failed to fetch" error.

Fix

Drop the Accept header. With only safelisted headers + credentials: 'include', the POST becomes a "simple cross-origin request" — no preflight required. The api returns JSON regardless of the request Accept value.

Test coverage block (rule 17)

Symptom: POST /auth/exchange returns net::ERR_FAILED in browser; CORS preflight 403 confirmed via curl
Enumeration: rg -nF 'corsAllowHeaders' internal/router/router.go (1 hit, allowlist excludes Accept)
Sites found: 1 (LoginCallbackPage.tsx exchangeCookieForToken)
Sites touched: 1
Coverage test: new regression guard asserts init.headers is undefined — catches any future reintroduction of Accept/Content-Type that would re-break the preflight
Live verified: will re-verify with chrome MCP after merge + GH Pages deploy

Why didn't #150 catch this

Unit tests mock fetch so they never exercise the real browser's CORS preflight logic. This is a class of bug that only surfaces in live cross-origin tests. CLAUDE.md rule 13 is explicit: "Live-URL gate. Every user-facing change ends with one of: (a) curl of the prod URL grep'd for the new SHA/content, (b) a real triggered email/webhook/job artifact, (c) explicit 'awaiting user verification of X'. Log greps DO NOT satisfy this gate." I treated npm run gate green as sufficient — that's the mistake.

🤖 Generated with Claude Code

…(P0 follow-up)

Follow-up to #150. chrome MCP live verification showed POST /auth/exchange
returning net::ERR_FAILED — root cause: the `Accept: application/json`
header on the fetch forced a CORS preflight. OPTIONS preflight 403'd
because `Accept` isn't in the api's `corsAllowHeaders` allowlist
(Content-Type, Authorization, X-Request-ID, X-E2E-Test-Token,
X-E2E-Source-IP). Confirmed with direct curl: OPTIONS /auth/exchange
+ Access-Control-Request-Headers: accept → 403 Forbidden.

Fix: drop the Accept header. A POST with only safelisted headers +
credentials:include is a "simple cross-origin request" — no preflight
needed. The api returns JSON regardless of the request Accept value.

Regression test: assert init.headers is undefined so a future
maintainer who reintroduces Accept / Content-Type gets caught by the
existing test pass before merge.

`npm run gate` clean locally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

size-limit report 📦

Path Size
dist/assets/index-CURDTYRC.js 0 B (-100% 🔽)
dist/assets/index-BsJUZYRr.css 6.13 KB (0%)
dist/assets/index-Di-yYCRi.js 163.67 KB (+100% 🔺)

@mastermanas805 mastermanas805 merged commit eec5ab9 into main May 30, 2026
16 checks passed
@mastermanas805 mastermanas805 deleted the fix/login-callback-no-accept-header branch May 30, 2026 09:34
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.

1 participant