Skip to content

feat: 2fa backup codes - #1

Open
ShashankFC wants to merge 1 commit into
enhance-two-factor-security-foundationfrom
improve-two-factor-authentication-features
Open

feat: 2fa backup codes#1
ShashankFC wants to merge 1 commit into
enhance-two-factor-security-foundationfrom
improve-two-factor-authentication-features

Conversation

@ShashankFC

@ShashankFC ShashankFC commented Feb 4, 2026

Copy link
Copy Markdown

Test 3

Summary by CodeRabbit

Release Notes

  • New Features
    • Added backup codes as a secondary two-factor authentication method
    • Users can download, copy, and view backup codes when enabling 2FA
    • Added "Lost Access" recovery option allowing users to authenticate with backup codes if their authenticator app is unavailable
    • Users can disable 2FA using backup codes if they've lost access to their primary method

✏️ Tip: You can customize this high-level summary in your review settings.


Replicated from ai-code-review-evaluation/cal.com-coderabbit#3


EntelligenceAI PR Summary

This PR adds backup code support for two-factor authentication, allowing users to generate and use single-use backup codes as an alternative authentication method.

  • Generated 10 cryptographically secure backup codes during 2FA setup with encryption using CALENDSO_ENCRYPTION_KEY
  • Added database schema changes with new backupCodes TEXT column in User model
  • Implemented UI components for backup code input, display, copy-to-clipboard, and download functionality
  • Enhanced login, 2FA setup, and 2FA disable flows to support backup code authentication with "Lost Access" toggle
  • Added API endpoints for backup code generation, validation, and used code removal
  • Introduced new error codes (IncorrectBackupCode, MissingBackupCodes) with localization support
  • Added E2E tests for backup code download and copy functionality
  • Updated test builders and improved password field UX with tabIndex adjustment

Co-authored-by: Peer Richelsen <peeroke@gmail.com>

@entelligence-ai-pr-reviews entelligence-ai-pr-reviews Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Walkthrough

This PR implements backup code functionality for two-factor authentication (2FA) across the Cal.com application. Users can now generate 10 single-use backup codes during 2FA setup, which can be downloaded or copied to clipboard. These backup codes serve as an alternative authentication method when the primary TOTP authenticator is unavailable. The implementation includes UI components for backup code input, API endpoints for generation and validation, database schema changes to store encrypted backup codes, and comprehensive integration across login, 2FA setup, and 2FA disable flows. End-to-end tests verify the download and copy functionality, and localization strings support the new user-facing features.

Changes

File(s) Summary
packages/prisma/schema.prisma
packages/prisma/migrations/20230804153419_add_backup_codes/migration.sql
Added backupCodes field to the User model as an optional TEXT column to store encrypted backup authentication codes.
packages/features/auth/lib/ErrorCode.ts Added two new error codes: IncorrectBackupCode and MissingBackupCodes for backup code validation.
apps/web/components/auth/BackupCode.tsx Created new React component for backup code input with form validation (10-11 characters) and optional centering layout.
apps/web/components/auth/TwoFactor.tsx Added optional autoFocus prop (default true) to control auto-focus behavior on the first input field.
apps/web/components/settings/EnableTwoFactorModal.tsx Added backup codes display step after 2FA setup with copy-to-clipboard and download functionality, replaced TextField with PasswordField for password input.
apps/web/components/settings/DisableTwoFactorModal.tsx Implemented backup code support as alternative authentication method with "Lost Access" toggle between TOTP and backup code modes.
apps/web/components/settings/TwoFactorAuthAPI.ts Modified disable method to accept backupCode parameter and include it in the API request payload.
apps/web/pages/api/auth/two-factor/totp/setup.ts Added generation of 10 cryptographically secure backup codes, encrypted storage in database, and inclusion in API response.
apps/web/pages/api/auth/two-factor/totp/disable.ts Enhanced disable endpoint to validate backup codes as alternative to TOTP codes, with decryption and matching logic, and cleanup of backup codes on disable.
apps/web/pages/auth/login.tsx Implemented backup code authentication flow with lost access toggle, new backupCode field, conditional component rendering, and error handling for backup code errors.
packages/features/auth/lib/next-auth-options.ts Added backup code credential field, validation logic with decryption, and removal of used backup codes from encrypted list upon successful authentication.
apps/web/public/static/locales/en/common.json Added seven i18n keys for backup code labels, instructions, success/error messages, and warnings.
apps/web/playwright/login.2fa.e2e.ts Added E2E tests for backup code download, copy-to-clipboard functionality, and dialog closure with TODO for additional scenarios.
packages/lib/test/builder.ts Added backupCodes property initialized to null in buildUser test helper function.
packages/ui/components/form/inputs/Input.tsx Added tabIndex={-1} to password visibility toggle button to exclude it from keyboard tab navigation.

Sequence Diagram

This diagram shows the interactions between components:

sequenceDiagram
    actor User
    participant TwoFactor as TwoFactor Component
    participant FormContext as React Hook Form Context
    participant LocaleHook as useLocale Hook
    participant TextField as TextField Component

    User->>TwoFactor: Render component (center prop)
    activate TwoFactor
    
    TwoFactor->>LocaleHook: useLocale()
    activate LocaleHook
    LocaleHook-->>TwoFactor: Return translation function (t)
    deactivate LocaleHook
    
    TwoFactor->>FormContext: useFormContext()
    activate FormContext
    FormContext-->>TwoFactor: Return form methods (register, etc.)
    deactivate FormContext
    
    Note over TwoFactor: Render UI with translations:<br/>- backup_code label<br/>- backup_code_instructions text
    
    TwoFactor->>TextField: Render with props
    activate TextField
    Note over TextField: Props: id="backup-code"<br/>placeholder="XXXXX-XXXXX"<br/>minLength=10, maxLength=11<br/>required=true
    
    TwoFactor->>FormContext: methods.register("backupCode")
    activate FormContext
    FormContext-->>TextField: Register field with form
    Note over FormContext,TextField: Field "backupCode" now<br/>controlled by form context
    deactivate FormContext
    
    TextField-->>TwoFactor: Rendered input field
    deactivate TextField
    
    TwoFactor-->>User: Display backup code input form
    deactivate TwoFactor
    
    User->>TextField: Enter backup code
    TextField->>FormContext: Update form state
    FormContext-->>TextField: Acknowledge update
Loading

🔗 Cross-Repository Impact Analysis

Enable automatic detection of breaking changes across your dependent repositories. → Set up now

Learn more about Cross-Repository Analysis

What It Does

  • Automatically identifies repositories that depend on this code
  • Analyzes potential breaking changes across your entire codebase
  • Provides risk assessment before merging to prevent cross-repo issues

How to Enable

  1. Visit Settings → Code Management
  2. Configure repository dependencies
  3. Future PRs will automatically include cross-repo impact analysis!

Benefits

  • 🛡️ Prevent breaking changes across repositories
  • 🔍 Catch integration issues before they reach production
  • 📊 Better visibility into your multi-repo architecture

▶️AI Code Reviews for VS Code, Cursor, Windsurf
Install the extension

Note for Windsurf Please change the default marketplace provider to the following in the windsurf settings:

Marketplace Extension Gallery Service URL: https://marketplace.visualstudio.com/_apis/public/gallery

Marketplace Gallery Item URL: https://marketplace.visualstudio.com/items

Entelligence.ai can learn from your feedback. Simply add 👍 / 👎 emojis to teach it your preferences. More shortcuts below

Emoji Descriptions:

  • ⚠️ Potential Issue - May require further investigation.
  • 🔒 Security Vulnerability - Fix to ensure system safety.
  • 💻 Code Improvement - Suggestions to enhance code quality.
  • 🔨 Refactor Suggestion - Recommendations for restructuring code.
  • ℹ️ Others - General comments and information.

Interact with the Bot:

  • Send a message or request using the format:
    @entelligenceai + *your message*
Example: @entelligenceai Can you suggest improvements for this code?
  • Help the Bot learn by providing feedback on its responses.
    @entelligenceai + *feedback*
Example: @entelligenceai Do not comment on `save_auth` function !

Also you can trigger various commands with the bot by doing
@entelligenceai command

The current supported commands are

  1. config - shows the current config
  2. retrigger_review - retriggers the review

More commands to be added soon.


if (response.status === 200) {
onEnable();
setStep(SetupStep.DisplayBackupCodes);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Correctness: In handleEnable, removing onEnable() causes a state desync. If the user dismisses the modal via ESC or backdrop after 2FA is enabled but before clicking the 'Close' button, the parent component is not notified and the UI remains stale. onEnable() must be invoked upon a successful API response to synchronize the parent state.

🤖 AI Agent Prompt for Cursor/Windsurf

📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue

In `apps/web/components/settings/EnableTwoFactorModal.tsx` at the success branch inside `handleEnable`, restore the `onEnable()` callback before advancing to `DisplayBackupCodes` so the parent is notified immediately after enabling 2FA. Ensure this callback fires regardless of how the modal is closed.

Comment on lines +149 to +156
await prisma.user.update({
where: {
id: user.id,
},
data: {
backupCodes: symmetricEncrypt(JSON.stringify(backupCodes), process.env.CALENDSO_ENCRYPTION_KEY),
},
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Correctness: The backup code invalidation is not atomic. Concurrent login requests can fetch the same user.backupCodes state, verify the same code, and both succeed before prisma.user.update persists the invalidation. This allows a single-use backup code to be reused. Wrap the verification and update in a database transaction with a row-level lock for atomicity.

🤖 AI Agent Prompt for Cursor/Windsurf

📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue

File: packages/features/auth/lib/next-auth-options.ts. At lines ~149-156, the backup code is validated then updated without atomic protection. Update the logic so consumption of a backup code is atomic (e.g., transaction with row lock or updateMany with a guard on the original encrypted backupCodes and fail if no row is updated). Ensure concurrent logins cannot reuse the same backup code.

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.

2 participants