From a38031caf800ecc269d12f01c9cc35f0e3e8be34 Mon Sep 17 00:00:00 2001 From: wangsijie Date: Thu, 12 Dec 2024 14:40:50 +0800 Subject: [PATCH] feat(core,console): new mfa prompt policy --- .changeset/hot-oranges-join.md | 19 +++++ .../src/pages/Mfa/MfaForm/constants.ts | 7 -- .../console/src/pages/Mfa/MfaForm/index.tsx | 67 ++++++++++----- .../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, 289 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..e5ddcb888 --- /dev/null +++ b/.changeset/hot-oranges-join.md @@ -0,0 +1,19 @@ +--- +"@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: +1. MfaPolicy.Mandatory: MFA is required for all users +2. MfaPolicy.NoPrompt: Do not ask users to set up MFA - new +3. MfaPolicy.PromptAtSignInAndSignUp: Ask users to set up MFA during registration (skippable, one-time prompt) - new +4. MfaPolicy.PromptOnlyAtSignIn: Ask users to set up MFA on their sign-in after registration (skippable, one-time prompt) - the old policy + +`MfaPolicy.UserControlled` is deprecated, use `MfaPolicy.PromptAtSignInAndSignUp` instead. 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..9b637f58f 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,21 @@ function MfaForm({ data, onMfaUpdated }: Props) { return factors.length === 0; }, [formValues, isMfaDisabled]); + const mfaPolicyOptions = [ + { + 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'), + }, + ]; + const onSubmit = handleSubmit( trySubmitSafe(async (formData) => { const mfaConfig = convertMfaFormToConfig(formData); @@ -143,28 +157,37 @@ function MfaForm({ data, onMfaUpdated }: Props) { /> )} - - - ( - - {Object.values(MfaPolicy).map((policy) => { - const title = policyOptionTitleMap[policy]; - return ( - - ); - })} - - )} + + + + {!formValues.isMandatory && ( + + ( +