fix(auth-callback): handle expired-link errors + HashRouter docs#79
fix(auth-callback): handle expired-link errors + HashRouter docs#79
Conversation
Capture the design for making AuthCallback detect Supabase auth errors from URL params (fast, specific messages), softening the hard 5s timeout into a two-stage 5s/30s behavior, and documenting correct redirectTo wiring for consumers that use HashRouter. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implementation plan for the 2026-04-17 spec. Ten bite-sized TDD tasks covering URL-error parsing (search, nested-hash, query-in-hash), error-code message mapping, two-stage 5s/30s timeout, short-circuit on URL errors, and Supabase.mdx HashRouter documentation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…erroring Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use toHaveBeenCalledWith(expect.any(Error)) instead of indexing into onError.mock.calls[0][0], which tsc rejects under noUncheckedIndexedAccess. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
✅ Deploy Preview for fluffy-ui ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughExtends the Changes
Sequence DiagramsequenceDiagram
actor User
participant AuthCallback
participant URLParser
participant MessageMapper
participant Callback as onError/onSuccess
participant Timers
participant Supabase as Supabase Auth
User->>AuthCallback: Mount component
AuthCallback->>URLParser: Parse window.location
alt Error params found in URL
URLParser->>MessageMapper: Map error_code to message
MessageMapper->>AuthCallback: Return user-facing message
AuthCallback->>Callback: Call onError(Error)
AuthCallback->>User: Render error state
else No URL error params
AuthCallback->>Timers: Start 5s soft timeout
AuthCallback->>Supabase: Subscribe to auth state
Timers->>AuthCallback: 5s elapsed
AuthCallback->>User: Render "taking longer" note
Timers->>Timers: Start 30s hard timeout
alt Auth succeeds before 30s
Supabase->>AuthCallback: SIGNED_IN event
AuthCallback->>Timers: Clear both timers
AuthCallback->>Callback: Call onSuccess()
AuthCallback->>User: Render success
else 30s timeout reached
Timers->>AuthCallback: Hard timeout
AuthCallback->>Callback: Call onError(expired)
AuthCallback->>User: Render timeout error
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/supabase/AuthCallback.tsx`:
- Around line 43-51: The authErrorMessage function currently returns description
?? fallback which treats an empty string as valid; update authErrorMessage to
trim the description and treat empty or whitespace-only strings as missing by
returning the generic message, e.g. if description is null or description.trim()
=== "" then return "Sign-in failed. Please try again.", otherwise return the
trimmed description; keep existing special-case checks for "otp_expired" and
"access_denied".
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0d86fb1a-33ae-4084-8a4d-c891f85e157c
📒 Files selected for processing (5)
docs/superpowers/plans/2026-04-17-auth-callback-url-errors.mddocs/superpowers/specs/2026-04-17-auth-callback-url-errors-design.mdlib/supabase/AuthCallback.test.tsxlib/supabase/AuthCallback.tsxlib/supabase/Supabase.mdx
URLSearchParams.get returns "" (not null) for present-but-empty params, so the previous `description ?? generic` would render an empty alert body. Trim and fall back to the generic message when empty or whitespace-only. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Fixes downstream HashRouter apps hitting
No routes matched location "/error=access_denied&error_code=otp_expired&..."when a user clicks an expired magic link.AuthCallbacknow parseserror/error_code/error_descriptionfrom both the URL's search string and hash (including the nested-hash shape HashRouter produces) on mount, and renders a specific error message immediately (otp_expired/access_deniedmapped to human-friendly text, falling back toerror_descriptionor a generic message).Supabase.mdxdocumenting the correctredirectTowiring (`${origin}/#/auth/callback`) and the pitfall that surfaces when the/#/prefix is omitted.No new exports, no new props on
AuthCallback, no change to the Supabase client flow.Spec:
docs/superpowers/specs/2026-04-17-auth-callback-url-errors-design.mdPlan:
docs/superpowers/plans/2026-04-17-auth-callback-url-errors.mdTest plan
npm test— 26/26 pass (10 AuthCallback tests including new URL-shape and timeout cases)npm run lint— cleannpx tsc --noEmit— clean🤖 Generated with Claude Code