Skip to content

fix: magic link login#133

Merged
gaboesquivel merged 1 commit into
mainfrom
magic-link
Mar 10, 2026
Merged

fix: magic link login#133
gaboesquivel merged 1 commit into
mainfrom
magic-link

Conversation

@gaboesquivel
Copy link
Copy Markdown
Member

@gaboesquivel gaboesquivel commented Mar 10, 2026

Summary by CodeRabbit

  • New Features

    • Magic links now support one-click sign-in when a verification token is included in the URL.
    • Added a fallback code entry form for manual verification when needed.
  • Documentation

    • Updated authentication flow diagrams to reflect the new verification process.
  • Tests

    • Enhanced test coverage for magic link verification flows.

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 10, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
basilic-docs Ready Ready Preview, Comment Mar 10, 2026 5:16pm
basilic-fastify Ready Ready Preview, Comment Mar 10, 2026 5:16pm
basilic-next Ready Ready Preview, Comment Mar 10, 2026 5:16pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 10, 2026

Walkthrough

This PR enhances the magic link authentication flow by adding a token query parameter to the callback URL alongside verificationId. The backend now includes the verification code in the magic link itself, while the frontend handler validates both parameters and provides a fallback code-entry form for manual verification.

Changes

Cohort / File(s) Summary
Documentation
apps/docu/content/docs/architecture/authentication.mdx
Updated sequence diagrams and flow references to reflect the new verificationId and token parameters in the callback URL and verify endpoint request.
Backend Magic Link
apps/fastify/src/routes/auth/magiclink/request.ts, apps/fastify/src/routes/auth/magiclink/request.test.ts
Modified the magic link generation to include the code as a token query parameter in the callback URL. Added test assertions to validate the token parameter is present in the generated link.
Frontend Callback Handler
apps/next/app/auth/callback/magiclink/route.ts
Implemented GET handler to validate verificationId and token from query params, with automatic verification if both are present or fallback to code-entry form. Added POST handler for form submissions with error mapping to specific error codes.
E2E Test Helpers
apps/next/e2e/auth-helpers.ts
Updated verifyMagicLink helper to include the token parameter in the verification URL and removed the UI-driven code-entry flow steps.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Email as Email Service
    participant Browser as Frontend<br/>(Next.js)
    participant Backend as Backend<br/>(Fastify)
    
    User->>Backend: POST /auth/magiclink/request
    Backend->>Backend: Generate verificationId, code
    Backend->>Email: Send email with<br/>link + code
    Email->>User: Email with magic link<br/>(?verificationId=...&token=...)
    
    alt Auto-verify (token present)
        User->>Browser: Click magic link
        Browser->>Backend: GET /auth/callback/magiclink<br/>(verificationId, token)
        Backend->>Backend: Verify credentials
        Backend-->>Browser: Auth tokens
        Browser->>Browser: Set cookies<br/>Redirect to callback URL
    else Manual entry (no token)
        User->>Browser: Visit callback URL<br/>(verificationId only)
        Browser->>Browser: Render code-entry form
        User->>Browser: Fill code & submit
        Browser->>Backend: POST /auth/callback/magiclink<br/>(verificationId, code)
        Backend->>Backend: Verify credentials
        Backend-->>Browser: Auth tokens
        Browser->>Browser: Set cookies<br/>Redirect to callback URL
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A token hops through the email breeze,
With verificationId and code with ease—
One-click or form, the choice is clear,
Magic links work better here! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

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.
Title check ❓ Inconclusive The title 'fix: magic link login' is vague and generic, failing to convey the specific nature of the changes made to the magic link authentication flow. Consider a more descriptive title such as 'fix: add token parameter to magic link callback flow' or 'fix: implement one-click magic link verification with token in URL' to clarify the specific changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch magic-link

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/docu/content/docs/architecture/authentication.mdx (1)

90-95: ⚠️ Potential issue | 🟡 Minor

Document manual verification as { email, token }.

apps/fastify/src/routes/auth/magiclink/verify.ts rejects requests that omit both verificationId and email, so step 3 is currently misleading. As per coding guidelines, **/*.mdx: Verify MDX content follows documentation standards.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/docu/content/docs/architecture/authentication.mdx` around lines 90 - 95,
The docs' "Option A (manual)" step is misleading: the verify endpoint
(apps/fastify/src/routes/auth/magiclink/verify.ts) requires either
verificationId or email, so update the MDX to document manual verification
payload as { email, token } (not just { token }) and show the correct POST to
/auth/magiclink/verify with that body; ensure the example matches the verify.ts
expectation and mention that verificationId may be used as an alternative.
apps/next/app/auth/callback/magiclink/route.ts (1)

25-52: ⚠️ Potential issue | 🟡 Minor

Surface INVALID_CODE in the fallback form.

The POST path already redirects back with message=INVALID_CODE, but this GET path never reads it and renderCodeEntryForm() always renders a blank form. Users get no feedback after a bad submission.

💡 Suggested fix
-function renderCodeEntryForm(verificationId: string, callbackURL: string): NextResponse {
+function renderCodeEntryForm(
+  verificationId: string,
+  callbackURL: string,
+  message?: string,
+): NextResponse {
+  const errorMessage =
+    message === 'INVALID_CODE' ? 'Enter a valid 6-digit code.' : undefined
   const html = `<!DOCTYPE html>
 <html lang="en">
 ...
 <body>
   <h1>Enter your code</h1>
   <p>Enter the 6-digit code from your email to sign in.</p>
   <form method="POST" action="/auth/callback/magiclink">
     <input type="hidden" name="verificationId" value="${verificationId.replace(/"/g, '&quot;')}"/>
     <input type="hidden" name="callbackURL" value="${callbackURL.replace(/"/g, '&quot;')}"/>
     <label for="token">Code</label>
     <input id="token" name="token" type="text" inputmode="numeric" pattern="\\d*" maxlength="6" autocomplete="one-time-code" placeholder="000000" required/>
+    ${errorMessage ? `<p class="error">${errorMessage}</p>` : ''}
     <button type="submit">Verify</button>
   </form>
 ...
 </html>`
 export async function GET(request: Request) {
   const { searchParams } = new URL(request.url)
   const verificationId = searchParams.get('verificationId')
   const token = searchParams.get('token')
   const rawCallback = searchParams.get('callbackURL')
+  const message = searchParams.get('message')
   const callbackURL = isSafeCallbackUrl(rawCallback, request.url)
 ...
   if (hasValidVerificationId && verificationId)
-    return renderCodeEntryForm(verificationId, callbackURL)
+    return renderCodeEntryForm(verificationId, callbackURL, message ?? undefined)

Also applies to: 55-59, 92-93

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/next/app/auth/callback/magiclink/route.ts` around lines 25 - 52, The
form never shows feedback for message=INVALID_CODE; update the GET handler to
read the request query param "message" and pass it into renderCodeEntryForm as
an optional errorMessage (or change renderCodeEntryForm signature to accept an
errorMessage string), then inside renderCodeEntryForm render a visible error
element (e.g., <p class="error">Invalid code — please try again</p>) when
errorMessage === "INVALID_CODE" (ensure you still escape verificationId and
callbackURL and place the error element near the token input so users see it);
apply the same change to the other call sites that render this form (the other
places referenced) so the INVALID_CODE message surfaces consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/next/app/auth/callback/magiclink/route.ts`:
- Around line 54-74: The GET handler currently performs a state-changing
magiclink exchange (calls client.auth.magiclink.verify, extracts tokens, sets
cookies via setAuthCookiesOnResponse and redirects) which must be removed: make
GET side-effect free by stopping any call to client.auth.magiclink.verify and
instead return a safe non-mutating response (e.g., a static confirmation page or
200 with instructions) and retain validation of verificationId, token and
callbackURL with isSafeCallbackUrl; then implement a POST handler (or an
explicit user action endpoint) that accepts verificationId and token from the
client, performs client.auth.magiclink.verify, calls extractTokens, sets auth
cookies with setAuthCookiesOnResponse and issues the redirect with
NextResponse.redirect, while preserving existing validation logic (uuidLike,
sixDigitCode) and error redirects (FAILED_VERIFY).

---

Outside diff comments:
In `@apps/docu/content/docs/architecture/authentication.mdx`:
- Around line 90-95: The docs' "Option A (manual)" step is misleading: the
verify endpoint (apps/fastify/src/routes/auth/magiclink/verify.ts) requires
either verificationId or email, so update the MDX to document manual
verification payload as { email, token } (not just { token }) and show the
correct POST to /auth/magiclink/verify with that body; ensure the example
matches the verify.ts expectation and mention that verificationId may be used as
an alternative.

In `@apps/next/app/auth/callback/magiclink/route.ts`:
- Around line 25-52: The form never shows feedback for message=INVALID_CODE;
update the GET handler to read the request query param "message" and pass it
into renderCodeEntryForm as an optional errorMessage (or change
renderCodeEntryForm signature to accept an errorMessage string), then inside
renderCodeEntryForm render a visible error element (e.g., <p
class="error">Invalid code — please try again</p>) when errorMessage ===
"INVALID_CODE" (ensure you still escape verificationId and callbackURL and place
the error element near the token input so users see it); apply the same change
to the other call sites that render this form (the other places referenced) so
the INVALID_CODE message surfaces consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 40eb9cca-74e0-4789-a42d-8d1cae2e6ddf

📥 Commits

Reviewing files that changed from the base of the PR and between 435e5b9 and d282602.

📒 Files selected for processing (5)
  • apps/docu/content/docs/architecture/authentication.mdx
  • apps/fastify/src/routes/auth/magiclink/request.test.ts
  • apps/fastify/src/routes/auth/magiclink/request.ts
  • apps/next/app/auth/callback/magiclink/route.ts
  • apps/next/e2e/auth-helpers.ts

Comment thread apps/next/app/auth/callback/magiclink/route.ts
@gaboesquivel gaboesquivel merged commit f96b173 into main Mar 10, 2026
9 checks passed
@gaboesquivel gaboesquivel deleted the magic-link branch March 10, 2026 17:24
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