fix: skip MFA binding prompt for UserControlled policy during sign-in
Logto returns 422 with an MFA recommendation when policy is UserControlled. Call POST /profile/mfa/mfa-skipped to skip the binding prompt, then re-submit. Users who already have MFA enrolled still get the TOTP verification flow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -71,20 +71,42 @@ export class MfaRequiredError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
async function trySubmit(): Promise<{ ok: true; redirectTo: string } | { ok: false; status: number; code: string; message: string }> {
|
||||
const res = await request('POST', '/submit');
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
return { ok: true, redirectTo: data.redirectTo };
|
||||
}
|
||||
const err = await res.json().catch(() => ({}));
|
||||
return { ok: false, status: res.status, code: err.code ?? '', message: err.message ?? `Submit failed (${res.status})` };
|
||||
}
|
||||
|
||||
async function skipMfaBinding(): Promise<void> {
|
||||
await request('POST', '/profile/mfa/mfa-skipped');
|
||||
}
|
||||
|
||||
export async function signIn(identifier: string, password: string): Promise<string> {
|
||||
await initInteraction();
|
||||
const verificationId = await verifyPassword(identifier, password);
|
||||
await identifyUser(verificationId);
|
||||
const res = await request('POST', '/submit');
|
||||
if (!res.ok) {
|
||||
const err = await res.json().catch(() => ({}));
|
||||
if (err.code === 'user.missing_mfa') {
|
||||
throw new MfaRequiredError();
|
||||
}
|
||||
throw new Error(err.message || `Submit failed (${res.status})`);
|
||||
const result = await trySubmit();
|
||||
if (result.ok) return result.redirectTo;
|
||||
|
||||
// MFA already enrolled — user must verify (show TOTP input)
|
||||
if (result.code === 'user.missing_mfa' || result.code === 'session.mfa.require_mfa_verification') {
|
||||
throw new MfaRequiredError();
|
||||
}
|
||||
const data = await res.json();
|
||||
return data.redirectTo;
|
||||
|
||||
// MFA not enrolled, UserControlled policy — skip the binding prompt.
|
||||
// Also fallback: any 422 with an MFA-related code we don't recognize — try skip before failing.
|
||||
if (result.status === 422 && result.code.includes('mfa')) {
|
||||
await skipMfaBinding();
|
||||
const retry = await trySubmit();
|
||||
if (retry.ok) return retry.redirectTo;
|
||||
throw new Error(retry.message);
|
||||
}
|
||||
|
||||
throw new Error(result.message);
|
||||
}
|
||||
|
||||
// --- Registration ---
|
||||
|
||||
Reference in New Issue
Block a user