Two-Factor Authentication (2FA)
Time-based One-Time Password (TOTP) implementation using the OTPAuth library.
Architecture
- Library:
otpauth(TOTP, SHA1, 6 digits, 30-second period) - Backup codes: 10 codes, 8-char hex via
crypto.randomBytes(4) - Storage: User model fields —
twoFactorSecret,twoFactorEnabled,twoFactorBackupCodes
Authentication Flow
Login Request
├─ Credentials valid + 2FA disabled → Return JWT tokens
└─ Credentials valid + 2FA enabled → Return { requiresTwoFactor: true, tempUserId }
└─ Client submits TOTP code
├─ Valid TOTP → Return JWT tokens
└─ Invalid TOTP → Check backup codes
├─ Valid backup → Consume code, return JWT tokens
└─ Invalid → 401 Unauthorized
API Endpoints
| Method | Endpoint | Description | Auth |
|---|---|---|---|
GET | /auth/2fa/setup | Generate QR code URI + secret | JWT |
POST | /auth/2fa/enable | Enable 2FA (requires valid token) | JWT |
DELETE | /auth/2fa/disable | Disable 2FA (requires valid token) | JWT |
POST | /auth/2fa/verify | Verify TOTP during login | Temp token |
Service Methods
TwoFactorService
Source: src/auth/two-factor.service.ts
| Method | Description |
|---|---|
generateSecret() | Creates TOTP secret, returns otpauth URI for QR |
verifyToken(secret, token) | Validates TOTP with window=5 (±2.5 minutes) |
enableTwoFactor(userId, token) | Verifies token then enables, returns backup codes |
disableTwoFactor(userId, token) | Verifies token then disables 2FA |
verifyTwoFactor(userId, token) | Checks TOTP first, then backup codes |
generateBackupCodes() | Generates 10 random 8-char hex codes |
regenerateBackupCodes(userId) | Replaces all backup codes |
Backup Codes
- Generated with
crypto.randomBytes(4).toString('hex')(8 hex characters each) - Stored as array on the User document
- Consumed on use — removed from array after successful verification
- Can be regenerated via
regenerateBackupCodes()
Security Notes
Critical
- TOTP secrets are stored in the database — encrypt at rest via MongoDB encryption
- Backup codes are single-use and removed from the array immediately
- Verification window of 5 allows for minor clock skew but increases brute-force surface
- The
tempUserIdreturned during 2FA challenge should have short TTL (5 minutes)
Related Modules
AuthModule— Login flow integrationUsersModule— User model with 2FA fields