0
Fork 0
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:
wangsijie 2023-10-17 14:18:53 +08:00 committed by GitHub
parent bfb1bf6d06
commit 15ab4d587e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 25 additions and 19 deletions

View file

@ -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] }
)
);
});

View file

@ -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) }
)
);
}

View file

@ -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
},

View file

@ -72,7 +72,7 @@ const mfaFactorsGuard = s.array(
);
export const missingMfaFactorsErrorDataGuard = s.object({
missingFactors: mfaFactorsGuard,
availableFactors: mfaFactorsGuard,
});
export const requireMfaFactorsErrorDataGuard = s.object({