Magic-Link — MFA & TOTP
Requirements
- Magic-Link Advanced tier ($39.50/month)
- Users need an authenticator app installed on their device
Enrollment flow
Each user enrolls once:
- User opens account settings → Enable MFA.
- Frontend calls
POST /api/magic-link/totp/enroll. - Response contains a QR code (otpauth URI) and 10 backup codes.
- User scans the QR with their authenticator app.
- User enters the current 6-digit code to confirm enrollment.
- TOTP is now required on every login.
API
bash
# Start enrollment
POST /api/magic-link/totp/enroll
Authorization: Bearer <existing-user-jwt>
→ 200 {
"qrCodeUri": "otpauth://totp/MagicDX:user@example.com?secret=...&issuer=MagicDX",
"secret": "JBSWY3DPEHPK3PXP",
"backupCodes": ["12345678", "23456789", ...]
}
# Confirm enrollment with first code
POST /api/magic-link/totp/confirm
{ "code": "123456" }
→ 200 { "enrolled": true }
# Login-time verification
POST /api/magic-link/totp/verify
{ "code": "123456" } // From authenticator app
→ 200 { "jwt": "..." }
# Use a backup code instead
POST /api/magic-link/totp/verify
{ "backupCode": "12345678" }
→ 200 { "jwt": "..." }
# Disable MFA (requires current TOTP)
POST /api/magic-link/totp/disable
{ "code": "123456" }
→ 200 { "disabled": true }MFA enforcement
In admin → Magic-Link → Settings → MFA:
- Off: MFA is optional per-user.
- Required for selected roles: Users with specified roles must enroll.
- Required for all users: Everyone must enroll on first login.
Supported authenticator apps
Any RFC 6238-compliant app works. Popular choices:
- Google Authenticator (free, iOS/Android)
- Authy (free, multi-device sync, iOS/Android/Desktop)
- 1Password (paid, excellent UX)
- Microsoft Authenticator (free, notification-based approval)
- Bitwarden (free, open-source)
- Aegis (free, open-source, Android)
Backup codes
10 one-time codes generated during enrollment. Store somewhere safe (password manager, printed, encrypted note). Each code works once; after use it's invalidated.
If all backup codes are exhausted, admin can regenerate via Admin → Users → Edit → Regenerate MFA Backup Codes.
Security considerations
- TOTP secret is encrypted at rest (AES-256-GCM).
- Backup codes are hashed (bcrypt) before storage — raw codes only shown during enrollment.
- Time drift tolerance: ±30 seconds (one step either side of current time).
- Rate limit: 5 code attempts per 60-second window, then 15-minute lockout.
Edge cases
User loses phone
- Use a backup code to log in.
- Regenerate TOTP (disable, re-enroll with new device).
If no backup codes remain:
- Admin can disable MFA on the user account via admin UI.
- User logs in via magic link, re-enrolls MFA on new device.
Clock drift
Authenticator and server clocks must be within ±30 seconds. If you suspect drift:
- Check server NTP.
- Check user's device time settings.
Multiple devices
TOTP secret is shared across devices — just scan the same QR on each authenticator app.
Next: API Reference →