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: { signInExperience: {
...mockSignInExperience, ...mockSignInExperience,
mfa: { mfa: {
factors: [MfaFactor.TOTP], factors: [MfaFactor.TOTP, MfaFactor.WebAuthn],
policy: MfaPolicy.Mandatory, policy: MfaPolicy.Mandatory,
}, },
}, },
@ -79,8 +79,11 @@ describe('validateMandatoryBindMfa', () => {
validateMandatoryBindMfa(tenantContext, mfaRequiredCtx, interaction) validateMandatoryBindMfa(tenantContext, mfaRequiredCtx, interaction)
).rejects.toMatchError( ).rejects.toMatchError(
new RequestError( 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) validateMandatoryBindMfa(tenantContext, mfaRequiredCtx, signInInteraction)
).rejects.toMatchError( ).rejects.toMatchError(
new RequestError( 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) { if (event === InteractionEvent.Register) {
const missingFactors = factors.filter((factor) => factor !== bindMfa?.type);
assertThat( assertThat(
missingFactors.length === 0, bindMfa && factors.includes(bindMfa.type),
new RequestError( new RequestError(
{ {
code: 'user.missing_mfa', code: 'user.missing_mfa',
status: 422, status: 422,
}, },
{ missingFactors } { availableFactors: factors.map((factor) => factor) }
) )
); );
} }
@ -99,17 +98,18 @@ export const validateMandatoryBindMfa = async (
if (event === InteractionEvent.SignIn) { if (event === InteractionEvent.SignIn) {
const { accountId } = interaction; const { accountId } = interaction;
const { mfaVerifications } = await tenant.queries.users.findUserById(accountId); const { mfaVerifications } = await tenant.queries.users.findUserById(accountId);
const missingFactors = factors.filter( const hasFactorInBind = Boolean(bindMfa && factors.includes(bindMfa.type));
(factor) => factor !== bindMfa?.type && !mfaVerifications.some(({ type }) => type === factor) const hasFactorInUser = factors.some((factor) =>
mfaVerifications.some(({ type }) => type === factor)
); );
assertThat( assertThat(
missingFactors.length === 0, hasFactorInBind || hasFactorInUser,
new RequestError( new RequestError(
{ {
code: 'user.missing_mfa', code: 'user.missing_mfa',
status: 422, status: 422,
}, },
{ missingFactors } { availableFactors: factors.map((factor) => factor) }
) )
); );
} }

View file

@ -27,23 +27,23 @@ const useMfaVerificationErrorHandler = ({ replace }: Options = {}) => {
() => ({ () => ({
'user.missing_mfa': (error) => { 'user.missing_mfa': (error) => {
const [_, data] = validate(error.data, missingMfaFactorsErrorDataGuard); 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); setToast(error.message);
return; return;
} }
if (missingFactors.length > 1) { if (availableFactors.length > 1) {
const state: MfaFactorsState = { availableFactors: missingFactors }; const state: MfaFactorsState = { availableFactors };
navigate({ pathname: `/${UserMfaFlow.MfaBinding}` }, { replace, state }); navigate({ pathname: `/${UserMfaFlow.MfaBinding}` }, { replace, state });
return; return;
} }
const factor = missingFactors[0]; const factor = availableFactors[0];
if (factor === MfaFactor.TOTP) { if (factor === MfaFactor.TOTP) {
void startTotpBinding(missingFactors); void startTotpBinding(availableFactors);
} }
// Todo: @xiaoyijun handle other factors // Todo: @xiaoyijun handle other factors
}, },

View file

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