Skip to content

[Wave 200pts] Implement TOTP-Based Two-Factor Authentication with Backup Codes, Recovery Flow, and E2E Tests #261

@portableDD

Description

@portableDD

Summary

OTP_SECRET is configured and validated in env.validation.ts but no 2FA system exists. The configuration is in place but unused.

Why This Matters

For a financial platform, 2FA is a standard security requirement — especially for admin users and large transaction approvals.

What Needs to Be Done

  • Build POST /auth/2fa/enable: generate TOTP secret, return QR code URI
  • Build POST /auth/2fa/verify: validate OTP and activate 2FA
  • Build POST /auth/2fa/disable: require current OTP to disable
  • Generate 10 single-use backup codes stored hashed
  • Build recovery flow for users who lose their authenticator app
  • Enforce 2FA on login — users with 2FA enabled must provide OTP
  • Write comprehensive E2E tests

Key Files

  • src/modules/auth/services/totp.service.ts — TOTP secret generation, QR URI, verification
  • src/modules/auth/auth.controller.ts — add 2fa endpoints
  • src/modules/users/entities/user.entity.ts — add twoFactorSecret, twoFactorEnabled, backupCodes fields
  • src/database/migrations/xxxx-add-2fa-fields.ts — migration
  • src/modules/auth/auth.service.ts — enforce 2FA check in login flow
  • test/two-factor-auth.e2e-spec.ts — E2E tests

Acceptance Criteria

  • POST /auth/2fa/enable returns TOTP secret and QR code URI
  • Login for 2FA users returns partial token requiring OTP confirmation
  • POST /auth/2fa/backup generates 10 single-use backup codes stored hashed
  • Recovery flow invalidates all backup codes and sends new ones via email
  • POST /auth/2fa/disable requires valid OTP
  • All E2E tests pass including invalid OTP, expired OTP, and used backup code scenarios

Constraints

  • TOTP secrets must be encrypted at rest
  • Backup codes must be hashed and single-use
  • QR code URI must be compatible with standard TOTP apps (Google Authenticator, Authy)
  • Complexity: High — 200 points

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions