From f1b1d9e95a3a01ec1ba5dc540836cbbd877f70fa Mon Sep 17 00:00:00 2001 From: wangsijie Date: Tue, 17 Dec 2024 19:51:09 +0400 Subject: [PATCH] feat(core,console): new mfa prompt policy (#6880) --- .changeset/hot-oranges-join.md | 24 ++++++ .../src/pages/Mfa/MfaForm/constants.ts | 7 -- .../console/src/pages/Mfa/MfaForm/index.tsx | 70 +++++++++++----- .../console/src/pages/Mfa/MfaForm/utils.ts | 17 ++-- packages/console/src/pages/Mfa/types.ts | 4 +- .../core/src/__mocks__/sign-in-experience.ts | 2 +- .../libraries/sign-in-experience/mfa.test.ts | 10 +-- .../core/src/routes/experience/classes/mfa.ts | 21 ++++- packages/core/src/routes/interaction/mfa.ts | 2 +- .../middleware/koa-interaction-sie.ts | 14 +++- .../mfa-verification.backup-code.test.ts | 2 +- .../verifications/mfa-verification.test.ts | 42 ++++++++-- .../verifications/mfa-verification.ts | 14 +++- .../experience-legacy/src/__mocks__/logto.tsx | 4 +- packages/experience/src/__mocks__/logto.tsx | 4 +- .../src/helpers/sign-in-experience.ts | 24 +++++- .../bind-mfa/happpy-path.test.ts | 84 ++++++++++++++++++- .../src/tests/api/sign-in-experience.test.ts | 2 +- .../en/translation/admin-console/mfa.ts | 9 ++ .../jsonb-types/sign-in-experience.ts | 8 ++ 20 files changed, 297 insertions(+), 67 deletions(-) create mode 100644 .changeset/hot-oranges-join.md delete mode 100644 packages/console/src/pages/Mfa/MfaForm/constants.ts diff --git a/.changeset/hot-oranges-join.md b/.changeset/hot-oranges-join.md new file mode 100644 index 000000000..9a0661a31 --- /dev/null +++ b/.changeset/hot-oranges-join.md @@ -0,0 +1,24 @@ +--- +"@logto/experience-legacy": minor +"@logto/integration-tests": minor +"@logto/experience": minor +"@logto/console": minor +"@logto/phrases": minor +"@logto/schemas": minor +"@logto/core": minor +--- + +new MFA prompt policy + +You can now cutomize the MFA prompt policy in the Console. + +First, choose if you want to enable **Require MFA**: + +- **Enable**: Users will be prompted to set up MFA during the sign-in process which cannot be skipped. If the user fails to set up MFA or deletes their MFA settings, they will be locked out of their account until they set up MFA again. +- **Disable**: Users can skip the MFA setup process during sign-up flow. + +If you choose to **Disable**, you can choose the MFA setup prompt: + +- Do not ask users to set up MFA. +- Ask users to set up MFA during registration (skippable, one-time prompt). **The same prompt as previous policy (UserControlled)** +- Ask users to set up MFA on their sign-in after registration (skippable, one-time prompt) diff --git a/packages/console/src/pages/Mfa/MfaForm/constants.ts b/packages/console/src/pages/Mfa/MfaForm/constants.ts deleted file mode 100644 index 4e291acbd..000000000 --- a/packages/console/src/pages/Mfa/MfaForm/constants.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { type AdminConsoleKey } from '@logto/phrases'; -import { MfaPolicy } from '@logto/schemas'; - -export const policyOptionTitleMap: Record = { - [MfaPolicy.UserControlled]: 'mfa.user_controlled', - [MfaPolicy.Mandatory]: 'mfa.mandatory', -}; diff --git a/packages/console/src/pages/Mfa/MfaForm/index.tsx b/packages/console/src/pages/Mfa/MfaForm/index.tsx index 9af5bb839..c2d8354e3 100644 --- a/packages/console/src/pages/Mfa/MfaForm/index.tsx +++ b/packages/console/src/pages/Mfa/MfaForm/index.tsx @@ -13,7 +13,7 @@ import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import DynamicT from '@/ds-components/DynamicT'; import FormField from '@/ds-components/FormField'; import InlineNotification from '@/ds-components/InlineNotification'; -import RadioGroup, { Radio } from '@/ds-components/RadioGroup'; +import Select from '@/ds-components/Select'; import Switch from '@/ds-components/Switch'; import useApi from '@/hooks/use-api'; import useDocumentationUrl from '@/hooks/use-documentation-url'; @@ -24,7 +24,6 @@ import { type MfaConfigForm, type MfaConfig } from '../types'; import FactorLabel from './FactorLabel'; import UpsellNotice from './UpsellNotice'; -import { policyOptionTitleMap } from './constants'; import styles from './index.module.scss'; import { convertMfaFormToConfig, convertMfaConfigToForm, validateBackupCodeFactor } from './utils'; @@ -69,6 +68,24 @@ function MfaForm({ data, onMfaUpdated }: Props) { return factors.length === 0; }, [formValues, isMfaDisabled]); + const mfaPolicyOptions = useMemo( + () => [ + { + value: MfaPolicy.NoPrompt, + title: t('mfa.no_prompt'), + }, + { + value: MfaPolicy.PromptAtSignInAndSignUp, + title: t('mfa.prompt_at_sign_in_and_sign_up'), + }, + { + value: MfaPolicy.PromptOnlyAtSignIn, + title: t('mfa.prompt_only_at_sign_in'), + }, + ], + [t] + ); + const onSubmit = handleSubmit( trySubmitSafe(async (formData) => { const mfaConfig = convertMfaFormToConfig(formData); @@ -143,28 +160,37 @@ function MfaForm({ data, onMfaUpdated }: Props) { /> )} - - - ( - - {Object.values(MfaPolicy).map((policy) => { - const title = policyOptionTitleMap[policy]; - return ( - - ); - })} - - )} + + + + {!formValues.isMandatory && ( + + ( +