feat: add optional TOTP MFA for email/password accounts
All checks were successful
CI / backend-tests (pull_request) Successful in 26s
CI / frontend-tests (pull_request) Successful in 28s

- Add MFA enrollment UI in new Settings page with QR code and backup secret
- Add TOTP challenge step to login flow for enrolled users
- Check AAL after login and show TOTP input when aal2 required
- Add disable MFA option with TOTP re-verification
- Only show MFA options for email/password users (not OAuth)
- Add Settings link to user dropdown menu

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 13:56:48 +01:00
parent a12958ae32
commit 7a828d7215
7 changed files with 610 additions and 11 deletions

View File

@@ -0,0 +1,56 @@
---
# nuzlocke-tracker-f2hs
title: Optional TOTP MFA for email/password accounts
status: in-progress
type: feature
priority: normal
created_at: 2026-03-21T12:19:18Z
updated_at: 2026-03-21T12:56:34Z
parent: nuzlocke-tracker-wwnu
---
## Problem
Users who sign up with email/password have no MFA option. Google/Discord OAuth users get their provider's MFA, but email-only users have a weaker security posture.
## Approach
Supabase has built-in TOTP MFA support via the `supabase.auth.mfa` API. This should be optional — users can enable it from their profile/settings page.
### Backend
- No backend changes needed — Supabase handles MFA enrollment and verification at the auth layer
- JWT tokens from MFA-enrolled users include an `aal` (authenticator assurance level) claim; optionally validate `aal2` for sensitive operations in the future
### Frontend
1. Add MFA setup flow to user profile/settings page:
- "Enable MFA" button → calls `supabase.auth.mfa.enroll({ factorType: 'totp' })`
- Show QR code from enrollment response
- Verify with TOTP code → `supabase.auth.mfa.challengeAndVerify()`
2. Add MFA challenge during login:
- After email/password sign-in, check `supabase.auth.mfa.getAuthenticatorAssuranceLevel()`
- If `currentLevel === 'aal1'` and `nextLevel === 'aal2'`, show TOTP input
- Verify → `supabase.auth.mfa.challengeAndVerify()`
3. Add "Disable MFA" option with re-verification
4. Only show MFA options for email/password users (not OAuth)
### UX
- Settings page: toggle to enable/disable MFA
- Login flow: TOTP input step after password for enrolled users
- Recovery: Supabase provides recovery codes during enrollment — display them
## Files to modify
- `frontend/src/pages/` — new MFA settings component or add to existing profile page
- `frontend/src/pages/Login.tsx` — add MFA challenge step
- `frontend/src/contexts/AuthContext.tsx` — handle AAL levels
## Checklist
- [x] Add MFA enrollment UI (QR code, verification) to profile/settings
- [x] Display backup secret code after enrollment (Supabase TOTP doesn't provide recovery codes)
- [x] Add TOTP challenge step to login flow
- [x] Check AAL after login and redirect to TOTP if needed
- [x] Add "Disable MFA" with re-verification
- [x] Only show MFA options for email/password users
- [ ] Test: full enrollment → login → TOTP flow
- [N/A] Test: recovery code works when TOTP unavailable (Supabase doesn't provide recovery codes; users save their secret key instead)