Skip to content

Commit 55cedcc

Browse files
authored
chore: remove email phone MFA feature flag (#7770)
* chore: remove feature flag of email phone mfa * refactor(schemas): set forgot password default value * fix(schemas): handle nullable columns with default values in schema generator * fix(schemas): fix alter * fix: fix integration tests
1 parent 6902c28 commit 55cedcc

File tree

33 files changed

+419
-514
lines changed

33 files changed

+419
-514
lines changed

packages/console/src/components/SignInExperiencePreview/index.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { useTranslation } from 'react-i18next';
99
import useSWR from 'swr';
1010

1111
import PhoneInfo from '@/assets/images/phone-info.svg?react';
12-
import { isDevFeaturesEnabled } from '@/consts/env';
1312
import { AppDataContext } from '@/contexts/AppDataProvider';
1413
import type { RequestError } from '@/hooks/use-api';
1514
import useUiLanguages from '@/hooks/use-ui-languages';
@@ -78,9 +77,9 @@ function SignInExperiencePreview({
7877
* This logic aligns with the core library implementation in sign-in-experience/index.ts
7978
*/
8079
const forgotPassword = (() => {
81-
// If forgotPasswordMethods is null (production compatibility) or dev features are not enabled,
80+
// If forgotPasswordMethods is null (production compatibility)
8281
// fall back to connector-based availability only
83-
if (!signInExperience.forgotPasswordMethods || !isDevFeaturesEnabled) {
82+
if (!signInExperience.forgotPasswordMethods) {
8483
return {
8584
email: hasEmailConnector,
8685
phone: hasSmsConnector,

packages/console/src/pages/Mfa/MfaForm/index.tsx

Lines changed: 46 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -217,60 +217,52 @@ function MfaForm({ data, signInMethods, onMfaUpdated }: Props) {
217217
label={<FactorLabel type={MfaFactor.TOTP} />}
218218
{...register('totpEnabled')}
219219
/>
220-
{isDevFeaturesEnabled && (
221-
<>
222-
<div>
223-
<Switch
224-
disabled={isMfaDisabled || isPhoneCodePrimarySignInMethod}
225-
label={<FactorLabel type={MfaFactor.PhoneVerificationCode} />}
226-
tooltip={
227-
isPhoneCodePrimarySignInMethod
228-
? t('mfa.phone_primary_method_tip')
229-
: undefined
230-
}
231-
{...register('phoneVerificationCodeEnabled')}
232-
/>
233-
{formValues.phoneVerificationCodeEnabled && !hasSmsConnector && (
234-
<InlineNotification className={styles.connectorWarning}>
235-
<Trans
236-
components={{
237-
a: <TextLink to="/connectors" />,
238-
}}
239-
>
240-
{t('mfa.no_sms_connector_warning', {
241-
link: t('mfa.setup_link'),
242-
})}
243-
</Trans>
244-
</InlineNotification>
245-
)}
246-
</div>
247-
<div>
248-
<Switch
249-
disabled={isMfaDisabled || isEmailCodePrimarySignInMethod}
250-
label={<FactorLabel type={MfaFactor.EmailVerificationCode} />}
251-
tooltip={
252-
isEmailCodePrimarySignInMethod
253-
? t('mfa.email_primary_method_tip')
254-
: undefined
255-
}
256-
{...register('emailVerificationCodeEnabled')}
257-
/>
258-
{formValues.emailVerificationCodeEnabled && !hasEmailConnector && (
259-
<InlineNotification className={styles.connectorWarning}>
260-
<Trans
261-
components={{
262-
a: <TextLink to="/connectors" />,
263-
}}
264-
>
265-
{t('mfa.no_email_connector_warning', {
266-
link: t('mfa.setup_link'),
267-
})}
268-
</Trans>
269-
</InlineNotification>
270-
)}
271-
</div>
272-
</>
273-
)}
220+
<div>
221+
<Switch
222+
disabled={isMfaDisabled || isPhoneCodePrimarySignInMethod}
223+
label={<FactorLabel type={MfaFactor.PhoneVerificationCode} />}
224+
tooltip={
225+
isPhoneCodePrimarySignInMethod ? t('mfa.phone_primary_method_tip') : undefined
226+
}
227+
{...register('phoneVerificationCodeEnabled')}
228+
/>
229+
{formValues.phoneVerificationCodeEnabled && !hasSmsConnector && (
230+
<InlineNotification className={styles.connectorWarning}>
231+
<Trans
232+
components={{
233+
a: <TextLink to="/connectors" />,
234+
}}
235+
>
236+
{t('mfa.no_sms_connector_warning', {
237+
link: t('mfa.setup_link'),
238+
})}
239+
</Trans>
240+
</InlineNotification>
241+
)}
242+
</div>
243+
<div>
244+
<Switch
245+
disabled={isMfaDisabled || isEmailCodePrimarySignInMethod}
246+
label={<FactorLabel type={MfaFactor.EmailVerificationCode} />}
247+
tooltip={
248+
isEmailCodePrimarySignInMethod ? t('mfa.email_primary_method_tip') : undefined
249+
}
250+
{...register('emailVerificationCodeEnabled')}
251+
/>
252+
{formValues.emailVerificationCodeEnabled && !hasEmailConnector && (
253+
<InlineNotification className={styles.connectorWarning}>
254+
<Trans
255+
components={{
256+
a: <TextLink to="/connectors" />,
257+
}}
258+
>
259+
{t('mfa.no_email_connector_warning', {
260+
link: t('mfa.setup_link'),
261+
})}
262+
</Trans>
263+
</InlineNotification>
264+
)}
265+
</div>
274266
<div className={styles.backupCodeField}>
275267
<div className={styles.backupCodeDescription}>
276268
<DynamicT forKey="mfa.backup_code_setup_hint" />

packages/console/src/pages/SignInExperience/PageContent/SignUpAndSignIn/SignInForm/index.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import type { SignInExperience } from '@logto/schemas';
22
import { useFormContext } from 'react-hook-form';
33
import { useTranslation } from 'react-i18next';
44

5-
import { isDevFeaturesEnabled } from '@/consts/env';
65
import Card from '@/ds-components/Card';
76
import FormField from '@/ds-components/FormField';
87

@@ -33,7 +32,7 @@ function SignInForm({ signInExperience }: Props) {
3332
</FormFieldDescription>
3433
<SignInMethodEditBox signInExperience={signInExperience} />
3534
</FormField>
36-
{isDevFeaturesEnabled && hasPasswordMethod && <ForgotPasswordMethodEditBox />}
35+
{hasPasswordMethod && <ForgotPasswordMethodEditBox />}
3736
</Card>
3837
);
3938
}

packages/console/src/pages/SignInExperience/PageContent/SignUpAndSignInChangePreview/SignUpAndSignInDiffSection/index.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { isDevFeaturesEnabled } from '@/consts/env';
21
import { type SignInExperiencePageManagedData } from '@/pages/SignInExperience/types';
32

43
import ForgotPasswordMethodsDiffSection from './ForgotPasswordMethodsDiffSection';
@@ -26,13 +25,11 @@ function SignUpAndSignInDiffSection({ before, after, isAfter = false }: Props) {
2625
after={after.socialSignInConnectorTargets}
2726
isAfter={isAfter}
2827
/>
29-
{isDevFeaturesEnabled && (
30-
<ForgotPasswordMethodsDiffSection
31-
before={before.forgotPasswordMethods ?? undefined}
32-
after={after.forgotPasswordMethods ?? undefined}
33-
isAfter={isAfter}
34-
/>
35-
)}
28+
<ForgotPasswordMethodsDiffSection
29+
before={before.forgotPasswordMethods ?? undefined}
30+
after={after.forgotPasswordMethods ?? undefined}
31+
isAfter={isAfter}
32+
/>
3633
</>
3734
);
3835
}

packages/console/src/pages/SignInExperience/PageContent/index.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { useParams } from 'react-router-dom';
88

99
import SubmitFormChangesActionBar from '@/components/SubmitFormChangesActionBar';
1010
import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal';
11-
import { isDevFeaturesEnabled } from '@/consts/env';
1211
import ConfirmModal from '@/ds-components/ConfirmModal';
1312
import TabNav, { TabNavItem } from '@/ds-components/TabNav';
1413
import useApi from '@/hooks/use-api';
@@ -132,10 +131,6 @@ function PageContent({ data, onSignInExperienceUpdated }: Props) {
132131

133132
// Forgot password migration from null to normal array
134133
useEffect(() => {
135-
if (!isDevFeaturesEnabled) {
136-
return;
137-
}
138-
139134
// Wait for connectors list loading to be ready
140135
if (!isConnectorsReady) {
141136
return;

packages/console/src/pages/SignInExperience/PageContent/utils/form.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import type { SignUp, ForgotPasswordMethod } from '@logto/schemas';
22
import { diff } from 'deep-object-diff';
33
import type { DeepRequired, FieldErrorsImpl } from 'react-hook-form';
44

5-
import { isDevFeaturesEnabled } from '@/consts/env';
6-
75
import type {
86
SignInExperienceForm,
97
SignInExperiencePageManagedData,
@@ -34,9 +32,7 @@ const hasSocialTargetsChanged = (before: string[], after: string[]) =>
3432
const hasForgotPasswordMethodsChanged = (
3533
before: readonly ForgotPasswordMethod[] | undefined,
3634
after: readonly ForgotPasswordMethod[] | undefined
37-
) =>
38-
isDevFeaturesEnabled &&
39-
Object.keys(diff((before ?? []).slice().sort(), (after ?? []).slice().sort())).length > 0;
35+
) => Object.keys(diff((before ?? []).slice().sort(), (after ?? []).slice().sort())).length > 0;
4036

4137
export const hasSignUpAndSignInConfigChanged = (
4238
before: SignInExperiencePageManagedData,

packages/console/src/pages/SignInExperience/PageContent/utils/parser.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
} from '@logto/schemas';
88
import { conditional } from '@silverhand/essentials';
99

10-
import { isDevFeaturesEnabled } from '@/consts/env';
1110
import { emptyBranding } from '@/types/sign-in-experience';
1211
import { removeFalsyValues } from '@/utils/object';
1312

@@ -148,8 +147,6 @@ export const sieFormDataParser = {
148147
signUp: signUpFormDataParser.toSignUp(signUp),
149148
signInMode: createAccountEnabled ? SignInMode.SignInAndRegister : SignInMode.SignIn,
150149
customCss: customCss?.length ? customCss : null,
151-
// TODO @wangsijie: Remove this once forgot password methods feature is ready for production.
152-
forgotPasswordMethods: isDevFeaturesEnabled ? forgotPasswordMethods : null,
153150
};
154151
},
155152
};

packages/core/src/libraries/sign-in-experience/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,9 +284,9 @@ export const createSignInExperienceLibrary = (
284284
const hasEmailConnector = logtoConnectors.some(({ type }) => type === ConnectorType.Email);
285285
const hasSmsConnector = logtoConnectors.some(({ type }) => type === ConnectorType.Sms);
286286

287-
// If forgotPasswordMethods is null (production compatibility) or dev features are not enabled,
287+
// If forgotPasswordMethods is null (production compatibility),
288288
// fall back to connector-based availability only
289-
if (!signInExperience.forgotPasswordMethods || !EnvSet.values.isDevFeaturesEnabled) {
289+
if (!signInExperience.forgotPasswordMethods) {
290290
return {
291291
email: hasEmailConnector,
292292
phone: hasSmsConnector,

packages/core/src/libraries/sign-in-experience/mfa.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import { MfaFactor, SignInIdentifier, type Mfa, type SignIn } from '@logto/schem
22

33
import assertThat from '#src/utils/assert-that.js';
44

5-
import { EnvSet } from '../../env-set/index.js';
6-
75
export const validateMfa = (mfa: Mfa, signIn?: SignIn) => {
86
assertThat(
97
new Set(mfa.factors).size === mfa.factors.length,
@@ -16,15 +14,6 @@ export const validateMfa = (mfa: Mfa, signIn?: SignIn) => {
1614
assertThat(mfa.factors.length > 1, 'sign_in_experiences.backup_code_cannot_be_enabled_alone');
1715
}
1816

19-
// TODO @wangsijie: Remove this guard when features are ready
20-
if (
21-
!EnvSet.values.isDevFeaturesEnabled &&
22-
(mfa.factors.includes(MfaFactor.EmailVerificationCode) ||
23-
mfa.factors.includes(MfaFactor.PhoneVerificationCode))
24-
) {
25-
throw new Error('Not implemented');
26-
}
27-
2817
// Validate that email/phone verification codes are not used as both sign-in method and MFA
2918
if (signIn && mfa.factors.includes(MfaFactor.EmailVerificationCode)) {
3019
const isEmailVerificationCodeSignIn = signIn.methods.some(

packages/core/src/routes/account/index.openapi.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@
147147
"get": {
148148
"operationId": "GetMfaSettings",
149149
"summary": "Get MFA settings",
150-
"tags": ["Dev feature"],
151150
"description": "Get MFA settings for the user. This endpoint requires the Identities scope. Returns current MFA configuration preferences.",
152151
"responses": {
153152
"200": {
@@ -161,7 +160,6 @@
161160
"patch": {
162161
"operationId": "UpdateMfaSettings",
163162
"summary": "Update MFA settings",
164-
"tags": ["Dev feature"],
165163
"description": "Update MFA settings for the user. This endpoint requires identity verification and the Identities scope. Controls whether MFA verification is required during sign-in when the user has MFA configured.",
166164
"responses": {
167165
"200": {

0 commit comments

Comments
 (0)