Auth: Add 2FA requirement to magic link login flow

Resolved 💬 3 comments Opened Nov 26, 2025 by cjamanambu Closed Jan 26, 2026

Summary

Add 2FA (TOTP) verification requirement to magic link login when users have mfaPreference.twoFA enabled, matching the security of password login.

Current Behavior

  • Password login (post-login-password.ts) checks mfaPreference.twoFA and requires TOTP before issuing JWT
  • Magic link login (post-verify-login.ts, post-verify-login-worker.ts) skips 2FA entirely and issues JWT immediately

Proposed Implementation

Use the Intermediate Session Token (IST) pattern (industry standard used by Stytch, Auth0):

Flow

  1. User clicks magic link
  2. POST /auth/v1/login/verify { magicToken }
  • If 2FA enabled: returns { intermediateToken, state: ONE, mfaRequired: true }
  • If no 2FA: returns { accessToken, state: TWO } (current behavior)
  1. POST /auth/v1/login/verify { intermediateToken, token: "123456" }
  • Validates IST + TOTP → returns { accessToken, state: TWO }

Why IST Pattern

  • Magic link stays one-time use (consumed immediately)
  • IST is short-lived (10 minutes) reducing attack window
  • Clean separation of authentication stages
  • Industry standard approach

Files to Modify

  • services/auth/handlers/post-verify-login.ts - Add 2FA check for integrators
  • services/auth/handlers/post-verify-login-worker.ts - Add 2FA check for workers
  • lib/schemas/models/session.ts - Add IST creation/validation functions
  • services/auth/schema/request/ - Update request schemas to accept intermediateToken + token

Acceptance Criteria

  • [ ] Magic link login requires 2FA when mfaPreference.twoFA is enabled
  • [ ] Intermediate tokens expire after 10 minutes
  • [ ] Intermediate tokens are one-time use
  • [ ] Existing flow unchanged when 2FA not enabled
  • [ ] Works for both integrators and workers/admins

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗