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 && ( + + ( +