Skip to content

fix(auth-callback): handle expired-link errors + HashRouter docs#79

Merged
tomusdrw merged 14 commits intomainfrom
td-magic-link-error
Apr 18, 2026
Merged

fix(auth-callback): handle expired-link errors + HashRouter docs#79
tomusdrw merged 14 commits intomainfrom
td-magic-link-error

Conversation

@tomusdrw
Copy link
Copy Markdown
Member

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.

  • AuthCallback now parses error / error_code / error_description from 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_denied mapped to human-friendly text, falling back to error_description or a generic message).
  • Replaces the hard 5s timeout with a two-stage pattern: a soft "This is taking longer than usual…" note at 5s, and the actual give-up error at 30s. Addresses the fact that slow PKCE code exchanges could take more than 5s and was masking legitimate sign-ins behind a bogus "expired" message.
  • Adds a Using HashRouter subsection to Supabase.mdx documenting the correct redirectTo wiring (`${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.md
Plan: docs/superpowers/plans/2026-04-17-auth-callback-url-errors.md

Test plan

  • npm test — 26/26 pass (10 AuthCallback tests including new URL-shape and timeout cases)
  • npm run lint — clean
  • npx tsc --noEmit — clean
  • Manually verify in a downstream HashRouter app: click an expired magic link, confirm the expired-link message renders immediately instead of the router "No routes matched" error
  • Manually verify a fresh magic link still signs in correctly and no longer shows the "taking longer" note under normal network conditions

🤖 Generated with Claude Code

tomusdrw and others added 13 commits April 17, 2026 16:03
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>
@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 17, 2026

Deploy Preview for fluffy-ui ready!

Name Link
🔨 Latest commit 2888612
🔍 Latest deploy log https://app.netlify.com/projects/fluffy-ui/deploys/69e37325682a20000810efbd
😎 Deploy Preview https://deploy-preview-79--fluffy-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 17, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d3811e1d-1175-45e1-9ca8-2892ad722122

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Extends the AuthCallback component to parse Supabase auth error parameters from URL search strings and hash segments, map error codes to user-facing messages, and implements a two-stage timeout flow (5-second soft notice, 30-second hard error). Includes test updates using fake timers and documentation clarification for HashRouter integration patterns.

Changes

Cohort / File(s) Summary
Documentation Plans & Specifications
docs/superpowers/plans/2026-04-17-auth-callback-url-errors.md, docs/superpowers/specs/2026-04-17-auth-callback-url-errors-design.md
New implementation plan and design specification documents detailing URL error parameter parsing, error message mapping, two-stage timeout restructuring, test coverage updates, and HashRouter documentation additions.
AuthCallback Component
lib/supabase/AuthCallback.tsx
Added URL error parameter detection (from window.location.search and window.location.hash), error-to-message mapping with fallback handling, early exit on URL errors, and revised timeout flow with 5-second soft indicator and 30-second hard timeout.
AuthCallback Tests
lib/supabase/AuthCallback.test.tsx
Integrated Vitest fake timers and lifecycle hooks; added test cases verifying immediate error rendering from various URL formats, onError callback invocation, timer progression at 5s and 30s milestones, and timer suppression when URL errors are present.
Supabase Documentation
lib/supabase/Supabase.mdx
Added "Using HashRouter" subsection with correct redirectTo format (/#/auth/callback), Supabase redirect allowlist requirements, route mounting example, and callout explaining failure modes when /#/ is omitted.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • feat: magic-link-first auth flow #77 — Introduced AuthCallback component with initial timeout behavior; this PR extends the same component to add URL-driven error handling and revise the timeout flow.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the two main changes: fixing expired-link error handling in AuthCallback and adding HashRouter documentation.
Description check ✅ Passed The description comprehensively explains the problem solved (HashRouter apps hitting route-not-found on expired links), the solution (URL error parsing and two-stage timeout), and the documentation changes, all aligned with the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch td-magic-link-error

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between a2067dd and a001b4b.

📒 Files selected for processing (5)
  • docs/superpowers/plans/2026-04-17-auth-callback-url-errors.md
  • docs/superpowers/specs/2026-04-17-auth-callback-url-errors-design.md
  • lib/supabase/AuthCallback.test.tsx
  • lib/supabase/AuthCallback.tsx
  • lib/supabase/Supabase.mdx

Comment thread lib/supabase/AuthCallback.tsx
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>
@tomusdrw tomusdrw merged commit 44e80c1 into main Apr 18, 2026
6 checks passed
@tomusdrw tomusdrw deleted the td-magic-link-error branch April 18, 2026 12:06
@github-actions github-actions Bot mentioned this pull request Apr 18, 2026
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