fix(auth): #154 make auth redirects basePath-aware (email confirm bounced to domain root)#156
Merged
Merged
Conversation
…nced to domain root) Auth flow built URLs from window.location.origin and hardcoded '/', ignoring the GitHub Pages basePath. On a project-site deploy the confirmation flow therefore stranded new users at the domain root. - add getInternalUrl/getRedirectUrl to project.config (trailing-slash canonical, degrade to old strings when basePath is empty) - inject the resolved basePath into the client bundle via next.config env (deploy workflows omit NEXT_PUBLIC_BASE_PATH on purpose, so the detected value never reached client code in production) - route emailRedirectTo, OAuth redirectTo, password-reset redirectTo and all hard navigations through the helpers - guard the cross-tab SIGNED_OUT redirect so it can never hijack /auth/callback (transient SIGNED_OUT during confirmation passes the token-validity check because no token exists yet) - pin the seam with unit tests: prefixed emailRedirectTo, prefixed signOut/cross-tab redirects, and no redirect on the callback page Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6 tasks
This was referenced Jul 3, 2026
main red: payment-isolation.spec.ts:113 fails on all 3 browsers after #156 auth-redirect change
#162
Closed
TortoiseWolfe
added a commit
that referenced
this pull request
Jul 3, 2026
…copes on rendered rows (#164) The payment-isolation "shows only own payments" E2E asserts `hasNoPayments || hasPayments`, counting `[data-payment-item]` for the list branch. PaymentHistory never emitted that attribute, so the test only passed via the empty-state — it went red the moment the shared fixture user acquired a seeded payment row (verified via the failure trace: query returned 200 with one row, component rendered the table, neither test branch matched). Not flake, not a regression from #156 — a latent test-selector gap. Confirmed pre-existing by re-running E2E on the pre-#156 commit (also red). Adds data-payment-item to the desktop table rows and mobile cards so the list branch matches actual rendered payments. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
4 tasks
TortoiseWolfe
added a commit
that referenced
this pull request
Jul 4, 2026
payment-isolation.spec.ts:113 went red on all 3 browsers on main after #156 shifted session/redirect timing. Root cause (reproduced): under concurrent CI backend load, getPaymentHistory()'s first attempt can hit a transient Realtime/RLS 401/406 during session hydration and throw. The component then rendered a THIRD terminal state — the 'Error loading payment history' alert — which is neither the empty-state heading nor a [data-payment-item], so the test's hasNoPayments||hasPayments assertion was false. Locally (uncontended backend) the test passes 9/9, confirming it's load-sensitivity, not broken logic. Fix (root cause, helps prod too): PaymentHistory.refetch now retries a failed fetch up to 3x with linear backoff (300/600ms) before surfacing the error alert — a momentary blip no longer strands the user on an error screen. The E2E assertion is also made resilient: expect.poll for a stable terminal state instead of asserting on the first post-loading frame, so it never races the retry window. Co-authored-by: TurtleWolfe <TurtleWolfe@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
3 tasks
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.
Closes #154
What was happening
On a GitHub Pages project-site deploy (any fork without a CNAME, e.g. RescueDogs at
tortoisewolfe.github.io/RescueDogs), a new user who clicked the Supabase confirmation email briefly landed in a signed-in state, then got bounced to the bare domain root where no app exists. Sign-up conversion was fully broken in production.Two defects stacked:
emailRedirectTowas${window.location.origin}/auth/callback(AuthContext.tsx), and the cross-tab sign-out handler plussignOut()didwindow.location.href = '/'— the domain root, not the app root. Same class of bug in the OAuthredirectTo, the password-resetredirectTo, and theverify-email/sign-in/ error-boundary hard navigations.NEXT_PUBLIC_BASE_PATHand rely onscripts/detect-project.js→project-detected.json→next.config.ts. That value configured the router, butgetProjectConfig()in the browser saw''. So even a "basePath-aware" URL builder would have produced the wrong URL in the deployed bundle.The visible bounce was the cross-tab SIGNED_OUT branch: during email confirmation no auth token exists yet, so the token-validity guard classifies the transient SIGNED_OUT as real and the handler hard-navigated to
'/'— off the app.The fix
src/config/project.config.ts: newgetInternalUrl(path)(basePath-prefixed, trailing-slash canonical —trailingSlash: trueexports emitroute/index.html, and Supabase's redirect allow-list is exact-match, so the slashless form costs a Pages 301 that can break allow-list matching) andgetRedirectUrl(path)(runtime origin + basePath, for Supabase redirect params). With an empty basePath both degrade to exactly the old strings.next.config.ts: inject the resolvedbasePathinto the client bundle via theenvblock. This is the load-bearing line for forks — verified by building with a basePath and findingbasePath:"/ScriptHammer"inlined in the emitted client chunk.AuthContextemailRedirectTo/signOut/cross-tab,OAuthButtons,ForgotPasswordForm,SignInForm,sign-uppage,reset-passwordpage,ErrorBoundary)./auth/callback— the callback page owns its own error handling, and a transient SIGNED_OUT there is expected during confirmation.Tests
src/config/__tests__/project.config.test.ts: helper behavior with/without basePath, trailing-slash canonicalization, query/hash passthrough, absolute redirect composition.tests/unit/auth/use-auth.test.tsx: withNEXT_PUBLIC_BASE_PATH=/RescueDogs— prefixedemailRedirectToat sign-up, prefixedsignOut()redirect, prefixed cross-tab SIGNED_OUT redirect, and no redirect when SIGNED_OUT fires on the callback page (the exact pre-fix bounce).Suite: unit tests, type-check, lint, gitleaks all green in the container. Existing E2E can't see this class of bug (it builds without a basePath) — follow-up issue coming for a basePath-enabled E2E project.
What this does NOT fix by itself
The Supabase project for each deployed fork needs its dashboard config to match (Site URL inside the app, prefixed callback URLs in the redirect allow-list). For RescueDogs (
cmdhajshektesctrappl): Site URLhttps://tortoisewolfe.github.io/RescueDogs, allow-list additionshttps://tortoisewolfe.github.io/RescueDogs/auth/callback/,https://tortoisewolfe.github.io/RescueDogs/reset-password/,https://tortoisewolfe.github.io/RescueDogs/**, plus the two localhost equivalents for dev. That project lives in a Supabase account the local tooling has no token for, so it's a dashboard task.Full-loop verification (real confirmation email → land under
/RescueDogs/auth/callback/→ profile) only closes in production after the RescueDogs cherry-pick deploys; not claiming it here.🤖 Generated with Claude Code