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

@@ -1,5 +1,5 @@
import { useState, useMemo } from 'react'
import { Link, Outlet, useLocation } from 'react-router-dom'
import { Link, Outlet, useLocation, useNavigate } from 'react-router-dom'
import { useTheme } from '../hooks/useTheme'
import { useAuth } from '../contexts/AuthContext'
@@ -67,6 +67,7 @@ function ThemeToggle() {
function UserMenu({ onAction }: { onAction?: () => void }) {
const { user, loading, signOut } = useAuth()
const [open, setOpen] = useState(false)
const navigate = useNavigate()
if (loading) {
return <div className="w-8 h-8 rounded-full bg-surface-3 animate-pulse" />
@@ -106,6 +107,17 @@ function UserMenu({ onAction }: { onAction?: () => void }) {
<p className="text-sm text-text-primary truncate">{email}</p>
</div>
<div className="py-1">
<button
type="button"
onClick={() => {
setOpen(false)
onAction?.()
navigate('/settings')
}}
className="w-full text-left px-4 py-2 text-sm text-text-secondary hover:text-text-primary hover:bg-surface-3 transition-colors"
>
Settings
</button>
<button
type="button"
onClick={async () => {