fix(auth): stamp session cookies onto redirect response after OAuth/magic link exchange - #832
Merged
Merged
Conversation
…xchange Next.js does not propagate cookies() mutations into NextResponse.redirect() automatically — the Set-Cookie headers were missing, so the browser never received session cookies despite Supabase confirming a successful login. Root cause confirmed via Supabase auth logs: /token returned 200 (login) on every Google OAuth and magic-link attempt, but users remained signed out. Changes: - Collect cookies in pendingCookies[] during setAll(), then explicitly stamp them onto the NextResponse.redirect() before returning - Reject //host double-slash bypass in `next` param open-redirect guard - Move cookieStore await after the oauthError early-return (avoid unnecessary work) - Log exchangeCodeForSession errors for production debugging - Replace filter/forEach with for..of to avoid intermediate array allocation - Await signInWithProvider() in LoginDrawer so errors surface via notifyError toast instead of being silently swallowed
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
tran-christian
added a commit
that referenced
this pull request
Apr 17, 2026
…st handling Replaces the third-attempt manual Set-Cookie stamping with Supabase's canonical @supabase/ssr App Router pattern and adds x-forwarded-host handling — the single constraint from the official example that has never been applied across #830, #832, or the current staged attempt. Root cause: on Vercel, new URL(request.url).origin can resolve to an internal load-balancer host rather than the public domain. When the redirect lands on a different origin than the one @supabase/ssr stamped Set-Cookie on, the browser appears to have lost the session. Changes: - Use createClient from @/lib/supabase/server (already canonical) instead of inlining createServerClient with a custom setAll - Drop pendingCookies[] / response.headers.append('Set-Cookie', ...) and the toSetCookieHeader() helper — Next.js propagates cookies().set() mutations onto the response on its own - Prefer x-forwarded-host in production redirects (canonical Supabase Next.js App Router example) - Keep /auth/callback proxy exclusion (from #830) so session-refresh does not clear the code_verifier - Keep a single _fh diagnostic param to confirm which redirect branch ran
tran-christian
added a commit
that referenced
this pull request
Apr 18, 2026
NextResponse.redirect(...) returns a standalone response that Next.js
forwards as-is — pending cookie mutations from cookies().set() are NOT
merged onto it. That explains why both the original canonical
cookieStore.set() pattern (Jan-Apr baseline) and the manual
response.cookies.set() / headers.append('Set-Cookie') stamping (#832,
earlier staged) have all failed to deliver session cookies in production.
redirect() from next/navigation instead throws NEXT_REDIRECT which the
framework catches and converts to a redirect response that DOES inherit
the cookie-mutation buffer. This is the pattern Supabase's canonical
/auth/confirm example uses.
Verified on preview: sb-*-auth-token-code-verifier landed on the domain
but sb-*-auth-token never did, matching the production symptom exactly.
Keep x-forwarded-host branch and _fh diagnostic; update doc block.
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root Cause
Confirmed via Supabase auth logs:
/token 200 loginfired on every Google OAuth and magic-link attempt — Supabase was succeeding. But users were never signed in.Next.js does not automatically propagate
cookies()mutations into aNextResponse.redirect()response.setAll()was writing to the internalcookieStore, but theSet-Cookieheaders never appeared on the outgoing redirect response. The browser never received the session.Changes
app/auth/callback/route.tspendingCookies[]duringsetAll(), then explicitly stamp each onto theNextResponse.redirect()before returning//hostdouble-slash bypass innextparam open-redirect guard (e.g.//evil.comstarts with/but is not a relative path)x-forwarded-hostredirect-base derivation — using untrusted request headers for the redirect destination is an open-redirect vector;new URL(request.url).originis correct on VercelexchangeCodeForSessionerrors for production debuggingcookieStoreawait after theoauthErrorearly-return to avoid unnecessary async work.filter().forEach()withfor...ofsrc/components/LoginDrawer.tsxawaitsignInWithProvider()so errors surface via the existingnotifyErrortoast instead of being silently swallowedVerification
/token 200 loginfor the account — Supabase was working, cookie delivery was the only gapmain(no merge conflicts)Separate issue (not in this PR)
The
500: Multiple accounts with the same email addresslog entry is from signing into GitHub with the same email already registered via Google. Fix in Supabase dashboard → Authentication → Providers → Link identities by email.