'use client'; import { useMemo, useState } from 'react'; import { useRouter } from 'next/navigation'; import { createSupabaseBrowserClient } from '@/lib/supabase/browser'; import { PasskeyEnrollPanel, RecoveryCodesPanel, TotpEnrollPanel, } from '../mfa-components'; /** * Drives the mandatory first-factor enrollment: pick TOTP and/or passkey, then * (once a factor is verified and the session is aal2) generate and display the * one-time recovery codes before releasing the user to the dashboard. */ export function EnrollClient() { const router = useRouter(); const supabase = useMemo(() => createSupabaseBrowserClient(), []); const [phase, setPhase] = useState<'choose' | 'recovery'>('choose'); const [codes, setCodes] = useState([]); const [error, setError] = useState(null); async function handleVerified() { setError(null); try { const res = await fetch('/api/security/recovery/generate', { method: 'POST', }); const json = (await res.json()) as { codes?: string[]; error?: string }; if (!res.ok || !json.codes) { throw new Error(json.error || 'Could not generate recovery codes.'); } setCodes(json.codes); } catch (e) { // The factor is already enabled; surface the error but still let the // user proceed (they can regenerate codes later in Security settings). setError( `${(e as Error).message} You can generate recovery codes later in Security settings.`, ); setCodes([]); } finally { setPhase('recovery'); } } function finish() { router.replace('/dashboard'); router.refresh(); } if (phase === 'recovery') { return (
{error &&

{error}

} {codes.length > 0 ? ( ) : (

Two-factor authentication enabled

Your account is now protected.

)}
); } return (
); }