Skip to content

Fix 2FA bypass vulnerability in NextAuth authorize callback #1

Description

@bgorzelic

Problem

The TOTP two-factor authentication check is decoupled from the NextAuth authorize callback. A determined attacker could skip the /two-factor page entirely and call signIn('credentials', ...) directly, bypassing TOTP verification.

Current Flow (Vulnerable)

  1. Client calls checkTwoFactorStatus(email, password) — validates credentials, returns twoFactorRequired: boolean
  2. If 2FA required, client redirects to /two-factor?email=...
  3. User enters TOTP code → verifyTwoFactorLogin() validates it
  4. On success, calls signIn('credentials', ...) to complete auth

The vulnerability: Step 4 calls signIn('credentials') which goes through the authorize callback in src/lib/auth.ts:32-57. This callback does NOT check twoFactorEnabled — it just validates email + password and returns the user.

Proposed Fix

Option A: Move TOTP into authorize callback

  • Accept a totpCode parameter in the credentials provider
  • In authorize(), after password validation, check if twoFactorEnabled is true
  • If enabled and no totpCode provided, throw an error
  • If enabled and totpCode provided, validate it via otpauth
  • The client sends email + password + TOTP code all in one signIn() call

Option B: Session-level 2FA state

  • Add a twoFactorVerified boolean to the JWT
  • Set it to false in authorize() when twoFactorEnabled is true
  • Middleware checks twoFactorVerified — if false, redirects to /two-factor
  • After TOTP verification, update the JWT to set twoFactorVerified: true

Option A is simpler and more secure (no intermediate session state). Recommended.

Acceptance Criteria

  • Cannot access protected routes without completing 2FA when enabled
  • Direct signIn('credentials', { email, password }) call without TOTP fails when 2FA is enabled
  • Login flow still works for users without 2FA enabled
  • /two-factor page still provides the UX for entering the code
  • Add test case documenting the fix

Files to Modify

  • apps/web/src/lib/auth.ts — authorize callback
  • apps/web/src/components/login-form.tsx — pass TOTP in signIn call
  • apps/web/src/app/two-factor/page.tsx — update flow
  • apps/web/src/components/settings/two-factor-setup.tsx — verify still works

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0: criticalCritical — security vulnerability or data lossarea: authAuthentication and authorizationsize: MMedium — 1-2 daystype: securitySecurity vulnerability or hardening

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions