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> {
|
export async function signIn(identifier: string, password: string): Promise<string> {
|
||||||
await initInteraction();
|
await initInteraction();
|
||||||
const verificationId = await verifyPassword(identifier, password);
|
const verificationId = await verifyPassword(identifier, password);
|
||||||
await identifyUser(verificationId);
|
await identifyUser(verificationId);
|
||||||
const res = await request('POST', '/submit');
|
const result = await trySubmit();
|
||||||
if (!res.ok) {
|
if (result.ok) return result.redirectTo;
|
||||||
const err = await res.json().catch(() => ({}));
|
|
||||||
if (err.code === 'user.missing_mfa') {
|
// MFA already enrolled — user must verify (show TOTP input)
|
||||||
throw new MfaRequiredError();
|
if (result.code === 'user.missing_mfa' || result.code === 'session.mfa.require_mfa_verification') {
|
||||||
}
|
throw new MfaRequiredError();
|
||||||
throw new Error(err.message || `Submit failed (${res.status})`);
|
|
||||||
}
|
}
|
||||||
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 ---
|
// --- Registration ---
|
||||||
|
|||||||
Reference in New Issue
Block a user