diff --git a/ui/sign-in/src/SignInPage.tsx b/ui/sign-in/src/SignInPage.tsx index a610227..5429d07 100644 --- a/ui/sign-in/src/SignInPage.tsx +++ b/ui/sign-in/src/SignInPage.tsx @@ -9,7 +9,7 @@ import { verifyTotp, verifyBackupCode, submitMfa, startWebAuthnAuth, verifyWebAuthnAuth, startWebAuthnRegistration, verifyWebAuthnRegistration, bindMfaProfile, - createTotpSecret, verifyTotpSetup, + generateBackupCodes, createTotpSecret, verifyTotpSetup, skipMfaEnrollment, submitInteraction, MfaRequiredError, MfaEnrollmentError, } from './experience-api'; @@ -318,7 +318,8 @@ export function SignInPage() { const credential = await startWebAuthnReg({ optionsJSON: registrationOptions as any }); const verifiedId = await verifyWebAuthnRegistration(verificationId, credential as unknown as Record); await bindMfaProfile('WebAuthn', verifiedId); - await bindMfaProfile('BackupCode'); + const bc = await generateBackupCodes(); + await bindMfaProfile('BackupCode', bc.verificationId); const result = await submitInteraction(); window.location.replace(result); } catch (err) { @@ -353,7 +354,8 @@ export function SignInPage() { try { const verifiedId = await verifyTotpSetup(totpCode); await bindMfaProfile('Totp', verifiedId); - await bindMfaProfile('BackupCode'); + const bc = await generateBackupCodes(); + await bindMfaProfile('BackupCode', bc.verificationId); const result = await submitInteraction(); window.location.replace(result); } catch (err) { diff --git a/ui/sign-in/src/experience-api.ts b/ui/sign-in/src/experience-api.ts index 30709ef..437721e 100644 --- a/ui/sign-in/src/experience-api.ts +++ b/ui/sign-in/src/experience-api.ts @@ -322,18 +322,25 @@ export async function verifyWebAuthnRegistration(verificationId: string, payload return data.verificationId; } -export async function bindMfaProfile(type: string, verificationId?: string): Promise { - const body: Record = { type }; - if (verificationId) body.verificationId = verificationId; - const res = await request('POST', '/profile/mfa', body); +export async function bindMfaProfile(type: string, verificationId: string): Promise { + const res = await request('POST', '/profile/mfa', { type, verificationId }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.message || `Failed to bind MFA (${res.status})`); } } +export async function generateBackupCodes(): Promise<{ verificationId: string; codes: string[] }> { + const res = await request('POST', '/verification/backup-code/generate'); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + throw new Error(err.message || `Failed to generate backup codes (${res.status})`); + } + return res.json(); +} + export async function createTotpSecret(): Promise<{ secret: string; secretQrCode: string; verificationId: string }> { - const res = await request('POST', '/verification/totp'); + const res = await request('POST', '/verification/totp/secret'); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.message || `Failed to create TOTP secret (${res.status})`);