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

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