feat(auth): mandatory 2FA (TOTP + WebAuthn passkeys) with hard enrollment gate, AAL2 step-up, and single-use recovery codes
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import { redirect } from 'next/navigation';
|
||||
import { createSupabaseServerClient } from '@/lib/supabase/server';
|
||||
import { EnrollClient } from './enroll-client';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
/**
|
||||
* Mandatory MFA enrollment gate. Reachable only by authenticated users (the
|
||||
* middleware sends users here when they have zero verified factors). If the
|
||||
* user already has a verified factor, there is nothing to enroll, so we send
|
||||
* them on to the dashboard.
|
||||
*/
|
||||
export default async function EnrollPage() {
|
||||
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') ?? [];
|
||||
if (verified.length > 0) redirect('/dashboard');
|
||||
|
||||
return (
|
||||
<main className="container">
|
||||
<h1>Secure your account</h1>
|
||||
<p className="muted">
|
||||
Two-factor authentication is required. Add at least one method below to
|
||||
continue.
|
||||
</p>
|
||||
<EnrollClient />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user