Two-Factor Authentication
Two-factor authentication (2FA) adds an extra security layer by requiring a TOTP code from an authenticator app in addition to the password.
How It Works
- Generate — server creates a TOTP secret and returns a QR code URL
- Scan — user scans the QR code with an authenticator app (Google Authenticator, Authy, etc.)
- Enable — user enters a valid TOTP code to confirm setup
- Login — after password verification, user must provide a TOTP code
API Endpoints
| Method | Path | Description |
|---|---|---|
POST | /api/auth/2fa/generate | Generate 2FA secret + QR code URL |
POST | /api/auth/2fa/enable | Enable 2FA (verify TOTP code) |
POST | /api/auth/2fa/disable | Disable 2FA (verify TOTP code) |
Prisma Model
2FA state is stored directly on the User model:
prisma
model User {
// ... other fields
twoFactorSecret String?
twoFactorEnabled Boolean @default(false)
}Frontend
TwoFactorSetup Component
The TwoFactorSetup.vue component in Settings provides:
- Generate button — calls
POST /api/auth/2fa/generate, displays QR code - Verify & Enable input — user enters TOTP code, calls
POST /api/auth/2fa/enable - Disable button — requires TOTP confirmation, calls
POST /api/auth/2fa/disable - Status indicator showing current 2FA state
Key Implementation Details
- The secret is generated server-side using the
speakeasylibrary - QR code URL follows the
otpauth://protocol for authenticator app compatibility - Enabling 2FA requires verifying a valid code first (prevents locking yourself out)
- Disabling 2FA also requires a valid TOTP code (prevents unauthorized removal)
- The secret is stored encrypted in the database