feat(auth): mandatory 2FA (TOTP + WebAuthn passkeys) with hard enrollment gate, AAL2 step-up, and single-use recovery codes

This commit is contained in:
Gerhard Scheikl
2026-05-31 21:38:01 +02:00
parent 129e21529c
commit e14e909700
19 changed files with 1310 additions and 142 deletions
+37
View File
@@ -0,0 +1,37 @@
import { redirect } from 'next/navigation';
import { createSupabaseServerClient } from '@/lib/supabase/server';
import { SecurityClient } from './security-client';
export const dynamic = 'force-dynamic';
/**
* Security settings: manage MFA factors and regenerate recovery codes. The
* middleware guarantees the visitor is authenticated and at aal2 (mandatory
* MFA), so factor mutations here always run with the required assurance level.
*/
export default async function SecurityPage() {
const supabase = createSupabaseServerClient();
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) redirect('/login');
const { data: factors } = await supabase.auth.mfa.listFactors();
const verified =
factors?.all
.filter((f) => f.status === 'verified')
.map((f) => ({
id: f.id,
type: f.factor_type,
friendlyName: f.friendly_name ?? null,
createdAt: f.created_at,
})) ?? [];
return (
<main className="container">
<h1>Security</h1>
<p className="muted">Manage two-factor authentication for your account.</p>
<SecurityClient initialFactors={verified} />
</main>
);
}