Skip to content

Commit e8df19b

Browse files
authored
feat(core): remove dev feature guard for email blocklist (#7397)
* feat(core): remove dev feature guard for email blocklist remove dev feature guard, enabeld email blocklist * chore: fix typo fix typo
1 parent 21a2ea7 commit e8df19b

File tree

11 files changed

+160
-147
lines changed

11 files changed

+160
-147
lines changed

.changeset/honest-news-rush.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@logto/core": minor
3+
---
4+
5+
feat: introduce email blocklist policy
6+
7+
We have added a new `emailBlocklistPolicy` in the `signInExperience` settings. This policy allows you to customize the email restriction rules for all users. Once this policy is set, users will be restricted from signing up or linking their accounts with any email addresses that are against the specified blocklist.
8+
This feature is particularly useful for organizations that want to prevent users from signing up with personal email addresses or any other specific domains.
9+
10+
Available settings include:
11+
12+
- `customBlocklist`: A custom blocklist of email addresses or domains that you want to restrict.
13+
- `blockSubaddressing`: Restrict email subaddressing (e.g., '[email protected]').

.changeset/soft-eggs-sell.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@logto/console": minor
3+
---
4+
5+
feat: introduce email blocklist settings page
6+
7+
Add a new email blocklist settings page to the Logto console under the Security section. This page allows administrators to manage the email blocklist policy for end users. Use this policy to restrict users from signing up or linking their accounts with any email addresses that are against the specified blocklist.

packages/console/src/hooks/use-console-routes/routes/security.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Navigate, type RouteObject } from 'react-router-dom';
22
import { safeLazy } from 'react-safe-lazy';
33

4-
import { isDevFeaturesEnabled } from '@/consts/env';
54
import { SecurityTabs } from '@/pages/Security/types';
65

76
const Security = safeLazy(async () => import('@/pages/Security'));
@@ -30,12 +29,7 @@ export const security: RouteObject = {
3029
},
3130
{
3231
path: SecurityTabs.Blocklist,
33-
// TODO: @simeng remove dev feature guard
34-
element: isDevFeaturesEnabled ? (
35-
<Security tab={SecurityTabs.Blocklist} />
36-
) : (
37-
<Navigate replace to={SecurityTabs.PasswordPolicy} />
38-
),
32+
element: <Security tab={SecurityTabs.Blocklist} />,
3933
},
4034
],
4135
};

packages/console/src/pages/Security/index.tsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import classNames from 'classnames';
22
import { useTranslation } from 'react-i18next';
33

44
import PageMeta from '@/components/PageMeta';
5-
import { isDevFeaturesEnabled } from '@/consts/env';
65
import { security } from '@/consts/external-links';
76
import CardTitle from '@/ds-components/CardTitle';
87
import TabNav, { TabNavItem } from '@/ds-components/TabNav';
@@ -45,15 +44,12 @@ function Security({ tab }: Props) {
4544
>
4645
{t('security.tabs.captcha')}
4746
</TabNavItem>
48-
{/** TODO: @simeng remove dev feature guard */}
49-
{isDevFeaturesEnabled && (
50-
<TabNavItem
51-
href={`/security/${SecurityTabs.Blocklist}`}
52-
isActive={tab === SecurityTabs.Blocklist}
53-
>
54-
{t('security.tabs.blocklist')}
55-
</TabNavItem>
56-
)}
47+
<TabNavItem
48+
href={`/security/${SecurityTabs.Blocklist}`}
49+
isActive={tab === SecurityTabs.Blocklist}
50+
>
51+
{t('security.tabs.blocklist')}
52+
</TabNavItem>
5753
<TabNavItem
5854
href={`/security/${SecurityTabs.General}`}
5955
isActive={tab === SecurityTabs.General}
@@ -64,8 +60,7 @@ function Security({ tab }: Props) {
6460
{tab === SecurityTabs.Captcha && <Captcha />}
6561
{tab === SecurityTabs.PasswordPolicy && <PasswordPolicy />}
6662
{tab === SecurityTabs.General && <General />}
67-
{/** TODO: @simeng remove dev feature guard */}
68-
{isDevFeaturesEnabled && tab === SecurityTabs.Blocklist && <Blocklist />}
63+
{tab === SecurityTabs.Blocklist && <Blocklist />}
6964
</div>
7065
);
7166
}

packages/core/src/libraries/sign-in-experience/email-blocklist-policy.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,6 @@ const parseCustomBlocklist = (customBlocklist: string[]) => {
4242
export const parseEmailBlocklistPolicy = (
4343
emailBlocklistPolicy: EmailBlocklistPolicy
4444
): EmailBlocklistPolicy => {
45-
// TODO: @simeng remove this validation when the feature is ready
46-
assertThat(
47-
EnvSet.values.isDevFeaturesEnabled,
48-
new RequestError({
49-
code: 'request.invalid_input',
50-
details: 'Email block list policy is not supported in this environment',
51-
})
52-
);
53-
5445
const { customBlocklist, ...rest } = emailBlocklistPolicy;
5546

5647
// BlockDisposableAddresses is not supported for OSS.

packages/core/src/routes/account/email-and-phone.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { z } from 'zod';
44

55
import koaGuard from '#src/middleware/koa-guard.js';
66

7-
import { EnvSet } from '../../env-set/index.js';
87
import RequestError from '../../errors/RequestError/index.js';
98
import { validateEmailAgainstBlocklistPolicy } from '../../libraries/sign-in-experience/email-blocklist-policy.js';
109
import { buildVerificationRecordByIdAndType } from '../../libraries/verification.js';
@@ -48,12 +47,9 @@ export default function emailAndPhoneRoutes<T extends UserRouter>(...args: Route
4847

4948
assertThat(scopes.has(UserScope.Email), 'auth.unauthorized');
5049

51-
// TODO: remove this when the feature is ready @simeng
5250
// Validate email blocklist policy
53-
if (EnvSet.values.isDevFeaturesEnabled) {
54-
const { emailBlocklistPolicy } = await findDefaultSignInExperience();
55-
await validateEmailAgainstBlocklistPolicy(emailBlocklistPolicy, email);
56-
}
51+
const { emailBlocklistPolicy } = await findDefaultSignInExperience();
52+
await validateEmailAgainstBlocklistPolicy(emailBlocklistPolicy, email);
5753

5854
// Check new identifier
5955
const newVerificationRecord = await buildVerificationRecordByIdAndType({

packages/core/src/routes/experience/classes/libraries/sign-in-experience-validator.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
VerificationType,
99
} from '@logto/schemas';
1010

11-
import { EnvSet } from '#src/env-set/index.js';
1211
import RequestError from '#src/errors/RequestError/index.js';
1312
import { validateEmailAgainstBlocklistPolicy } from '#src/libraries/sign-in-experience/index.js';
1413
import type Libraries from '#src/tenants/Libraries.js';
@@ -281,11 +280,6 @@ export class SignInExperienceValidator {
281280
* - guard custom email address/domain if provided
282281
*/
283282
public async guardEmailBlocklist(verificationRecord: VerificationRecord) {
284-
// TODO: Remove this once the dev feature is ready
285-
if (!EnvSet.values.isDevFeaturesEnabled) {
286-
return;
287-
}
288-
289283
const email = getEmailIdentifierFromVerificationRecord(verificationRecord);
290284
if (!email) {
291285
return;

packages/core/src/routes/sign-in-experience/index.openapi.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,20 @@
6666
},
6767
"sentinelPolicy": {
6868
"description": "Custom sentinel policy settings. Use this field to customize the user lockout policy. The default value is 100 failed attempts within one hour. The user will be locked out for 60 minutes after exceeding the limit."
69+
},
70+
"emailBlocklistPolicy": {
71+
"description": "Define email restriction policies. Users will be prohibited from registering or linking any email addresses that are included in the blocklist.",
72+
"properties": {
73+
"blockDisposableAddress": {
74+
"description": "Cloud only. Whether to block disposable email addresses. Once enabled, Logto will check the email domain against a list of known disposable email domains. If the domain is found in the list, the email address will be blocked."
75+
},
76+
"blockSubaddressing": {
77+
"description": "Whether to block sub-addresses. (E.g., [email protected])"
78+
},
79+
"customBlocklist": {
80+
"description": "Custom blocklist of email addresses or domains."
81+
}
82+
}
6983
}
7084
}
7185
}
@@ -135,6 +149,23 @@
135149
},
136150
"unknownSessionRedirectUrl": {
137151
"description": "The fallback URL to redirect users when the sign-in session does not exist or unknown. Client should initiate a new authentication flow after the redirection."
152+
},
153+
"sentinelPolicy": {
154+
"description": "Custom sentinel policy settings. Use this field to customize the user lockout policy. The default value is 100 failed attempts within one hour. The user will be locked out for 60 minutes after exceeding the limit."
155+
},
156+
"emailBlocklistPolicy": {
157+
"description": "Define email restriction policies. Users will be prohibited from registering or linking any email addresses that are included in the blocklist.",
158+
"properties": {
159+
"blockDisposableAddress": {
160+
"description": "Cloud only. Whether to block disposable email addresses. Once enabled, Logto will check the email domain against a list of known disposable email domains. If the domain is found in the list, the email address will be blocked."
161+
},
162+
"blockSubaddressing": {
163+
"description": "Whether to block sub-addresses. (E.g., [email protected])"
164+
},
165+
"customBlocklist": {
166+
"description": "Custom blocklist of email addresses or domains."
167+
}
168+
}
138169
}
139170
}
140171
}

packages/integration-tests/src/tests/api/account/email-and-phone.test.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,7 @@ import {
2525
signInAndGetUserApi,
2626
} from '#src/helpers/profile.js';
2727
import { enableAllPasswordSignInMethods } from '#src/helpers/sign-in-experience.js';
28-
import {
29-
devFeatureTest,
30-
generateEmail,
31-
generatePhone,
32-
generateNationalPhoneNumber,
33-
} from '#src/utils.js';
28+
import { generateEmail, generatePhone, generateNationalPhoneNumber } from '#src/utils.js';
3429

3530
describe('account (email and phone)', () => {
3631
beforeAll(async () => {
@@ -146,7 +141,7 @@ describe('account (email and phone)', () => {
146141
await deleteDefaultTenantUser(user.id);
147142
});
148143

149-
devFeatureTest.it('should reject the email if the email is in the blocklist', async () => {
144+
it('should reject the email if the email is in the blocklist', async () => {
150145
const email = generateEmail();
151146
await updateSignInExperience({
152147
emailBlocklistPolicy: {

0 commit comments

Comments
 (0)