mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
fix(core): validate mandatory mfa (#4639)
This commit is contained in:
parent
bfb1bf6d06
commit
15ab4d587e
4 changed files with 25 additions and 19 deletions
|
@ -51,7 +51,7 @@ const mfaRequiredCtx = {
|
|||
signInExperience: {
|
||||
...mockSignInExperience,
|
||||
mfa: {
|
||||
factors: [MfaFactor.TOTP],
|
||||
factors: [MfaFactor.TOTP, MfaFactor.WebAuthn],
|
||||
policy: MfaPolicy.Mandatory,
|
||||
},
|
||||
},
|
||||
|
@ -79,8 +79,11 @@ describe('validateMandatoryBindMfa', () => {
|
|||
validateMandatoryBindMfa(tenantContext, mfaRequiredCtx, interaction)
|
||||
).rejects.toMatchError(
|
||||
new RequestError(
|
||||
{ code: 'user.missing_mfa', status: 422 },
|
||||
{ missingFactors: [MfaFactor.TOTP] }
|
||||
{
|
||||
code: 'user.missing_mfa',
|
||||
status: 422,
|
||||
},
|
||||
{ availableFactors: [MfaFactor.TOTP, MfaFactor.WebAuthn] }
|
||||
)
|
||||
);
|
||||
});
|
||||
|
@ -111,8 +114,11 @@ describe('validateMandatoryBindMfa', () => {
|
|||
validateMandatoryBindMfa(tenantContext, mfaRequiredCtx, signInInteraction)
|
||||
).rejects.toMatchError(
|
||||
new RequestError(
|
||||
{ code: 'user.missing_mfa', status: 422 },
|
||||
{ missingFactors: [MfaFactor.TOTP] }
|
||||
{
|
||||
code: 'user.missing_mfa',
|
||||
status: 422,
|
||||
},
|
||||
{ availableFactors: [MfaFactor.TOTP, MfaFactor.WebAuthn] }
|
||||
)
|
||||
);
|
||||
});
|
||||
|
|
|
@ -83,15 +83,14 @@ export const validateMandatoryBindMfa = async (
|
|||
}
|
||||
|
||||
if (event === InteractionEvent.Register) {
|
||||
const missingFactors = factors.filter((factor) => factor !== bindMfa?.type);
|
||||
assertThat(
|
||||
missingFactors.length === 0,
|
||||
bindMfa && factors.includes(bindMfa.type),
|
||||
new RequestError(
|
||||
{
|
||||
code: 'user.missing_mfa',
|
||||
status: 422,
|
||||
},
|
||||
{ missingFactors }
|
||||
{ availableFactors: factors.map((factor) => factor) }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -99,17 +98,18 @@ export const validateMandatoryBindMfa = async (
|
|||
if (event === InteractionEvent.SignIn) {
|
||||
const { accountId } = interaction;
|
||||
const { mfaVerifications } = await tenant.queries.users.findUserById(accountId);
|
||||
const missingFactors = factors.filter(
|
||||
(factor) => factor !== bindMfa?.type && !mfaVerifications.some(({ type }) => type === factor)
|
||||
const hasFactorInBind = Boolean(bindMfa && factors.includes(bindMfa.type));
|
||||
const hasFactorInUser = factors.some((factor) =>
|
||||
mfaVerifications.some(({ type }) => type === factor)
|
||||
);
|
||||
assertThat(
|
||||
missingFactors.length === 0,
|
||||
hasFactorInBind || hasFactorInUser,
|
||||
new RequestError(
|
||||
{
|
||||
code: 'user.missing_mfa',
|
||||
status: 422,
|
||||
},
|
||||
{ missingFactors }
|
||||
{ availableFactors: factors.map((factor) => factor) }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -27,23 +27,23 @@ const useMfaVerificationErrorHandler = ({ replace }: Options = {}) => {
|
|||
() => ({
|
||||
'user.missing_mfa': (error) => {
|
||||
const [_, data] = validate(error.data, missingMfaFactorsErrorDataGuard);
|
||||
const missingFactors = data?.missingFactors ?? [];
|
||||
const availableFactors = data?.availableFactors ?? [];
|
||||
|
||||
if (missingFactors.length === 0) {
|
||||
if (availableFactors.length === 0) {
|
||||
setToast(error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (missingFactors.length > 1) {
|
||||
const state: MfaFactorsState = { availableFactors: missingFactors };
|
||||
if (availableFactors.length > 1) {
|
||||
const state: MfaFactorsState = { availableFactors };
|
||||
navigate({ pathname: `/${UserMfaFlow.MfaBinding}` }, { replace, state });
|
||||
return;
|
||||
}
|
||||
|
||||
const factor = missingFactors[0];
|
||||
const factor = availableFactors[0];
|
||||
|
||||
if (factor === MfaFactor.TOTP) {
|
||||
void startTotpBinding(missingFactors);
|
||||
void startTotpBinding(availableFactors);
|
||||
}
|
||||
// Todo: @xiaoyijun handle other factors
|
||||
},
|
||||
|
|
|
@ -72,7 +72,7 @@ const mfaFactorsGuard = s.array(
|
|||
);
|
||||
|
||||
export const missingMfaFactorsErrorDataGuard = s.object({
|
||||
missingFactors: mfaFactorsGuard,
|
||||
availableFactors: mfaFactorsGuard,
|
||||
});
|
||||
|
||||
export const requireMfaFactorsErrorDataGuard = s.object({
|
||||
|
|
Loading…
Reference in a new issue