Skip to content

Commit 2759ff8

Browse files
authored
feat(core,experience): pre validate email blocklist (#7395)
* feat(core,experience): pre validate email blocklist pre validate email against blocklist on user registraction before send verification code * style(experience): add danger style add danger style to the profile fulfillment form * fix(test): fix tests fix tests * chore: fix typo fix typo
1 parent 613305e commit 2759ff8

File tree

5 files changed

+34
-66
lines changed

5 files changed

+34
-66
lines changed

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

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ export const parseEmailBlocklistPolicy = (
4545
// TODO: @simeng remove this validation when the feature is ready
4646
assertThat(
4747
EnvSet.values.isDevFeaturesEnabled,
48-
new RequestError('request.invalid_input', {
48+
new RequestError({
49+
code: 'request.invalid_input',
4950
details: 'Email block list policy is not supported in this environment',
5051
})
5152
);
@@ -56,8 +57,9 @@ export const parseEmailBlocklistPolicy = (
5657
if (rest.blockDisposableAddresses) {
5758
assertThat(
5859
EnvSet.values.isCloud,
59-
new RequestError('request.invalid_input', {
60-
details: 'blockDisposableAddresses is not supported in this environment',
60+
new RequestError({
61+
code: 'request.invalid_input',
62+
details: 'Disposable email domain validation is not supported in this environment',
6163
})
6264
);
6365
}
@@ -74,7 +76,7 @@ const disposableEmailDomainValidationResponseGuard = z.object({
7476

7577
const validateDisposableEmailDomain = async (email: string) => {
7678
// TODO: Skip the validation for integration test for now
77-
if (EnvSet.values.isIntegrationTest) {
79+
if (EnvSet.values.isIntegrationTest || EnvSet.values.isUnitTest) {
7880
return;
7981
}
8082

@@ -143,11 +145,6 @@ export const validateEmailAgainstBlocklistPolicy = async (
143145

144146
assertThat(domain, new RequestError('session.email_blocklist.invalid_email'));
145147

146-
// Guard disposable email domain if enabled
147-
if (EnvSet.values.isCloud && blockDisposableAddresses) {
148-
await validateDisposableEmailDomain(email);
149-
}
150-
151148
// Guard email subaddressing if enabled
152149
if (blockSubaddressing) {
153150
const subaddressingRegex = new RegExp(`^.*\\+.*@${domain}$`);
@@ -180,6 +177,11 @@ export const validateEmailAgainstBlocklistPolicy = async (
180177
})
181178
);
182179
}
180+
181+
// Guard disposable email domain if enabled
182+
if (blockDisposableAddresses) {
183+
await validateDisposableEmailDomain(email);
184+
}
183185
};
184186

185187
export const isEmailBlocklistPolicyEnabled = (emailBlockListPolicy: EmailBlocklistPolicy) => {

packages/core/src/routes/experience/verification-routes/verification-code.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,16 @@ export default function verificationCodeRoutes<T extends ExperienceInteractionRo
101101
getTemplateTypeByEvent(interactionEvent)
102102
);
103103

104+
// Pre validate the email against email blocklist if the interaction event is register
105+
if (
106+
interactionEvent === InteractionEvent.Register &&
107+
identifier.type === SignInIdentifier.Email
108+
) {
109+
await ctx.experienceInteraction.signInExperienceValidator.guardEmailBlocklist(
110+
codeVerification
111+
);
112+
}
113+
104114
const templateContext = await buildVerificationCodeTemplateContext(
105115
libraries.passcodes,
106116
ctx,

packages/experience/src/hooks/use-send-verification-code.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ const useSendVerificationCode = (flow: UserFlow, replaceCurrentPage?: boolean) =
5656
identifier === SignInIdentifier.Email ? 'invalid_email' : 'invalid_phone'
5757
);
5858
},
59+
'session.email_blocklist.email_not_allowed': (error) => {
60+
setErrorMessage(error.message);
61+
},
62+
'session.email_blocklist.email_subaddressing_not_allowed': (error) => {
63+
setErrorMessage(error.message);
64+
},
5965
});
6066

6167
return;

packages/experience/src/pages/Continue/IdentifierProfileForm/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ const IdentifierProfileForm = ({
105105
autoFocus={autoFocus}
106106
className={styles.inputField}
107107
{...field}
108-
isDanger={!!errors.identifier}
108+
isDanger={!!errors.identifier || !!errorMessage}
109109
errorMessage={errors.identifier?.message}
110110
enabledTypes={enabledTypes}
111111
/>

packages/integration-tests/src/tests/api/experience-api/register-interaction/email-blocklist.test.ts

Lines changed: 6 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ import {
1414
successFullyCreateSocialVerification,
1515
successFullyVerifySocialAuthorization,
1616
} from '#src/helpers/experience/social-verification.js';
17-
import {
18-
successfullySendVerificationCode,
19-
successfullyVerifyVerificationCode,
20-
} from '#src/helpers/experience/verification-code.js';
2117
import { expectRejects } from '#src/helpers/index.js';
2218
import { devFeatureTest, generateEmail } from '#src/utils.js';
2319

@@ -57,33 +53,10 @@ devFeatureTest.describe(
5753
interactionEvent: InteractionEvent.Register,
5854
});
5955

60-
const { verificationId, code } = await successfullySendVerificationCode(client, {
61-
identifier,
62-
interactionEvent: InteractionEvent.Register,
63-
});
64-
65-
await successfullyVerifyVerificationCode(client, {
66-
identifier,
67-
verificationId,
68-
code,
69-
});
70-
71-
// Should reject the registration directly
7256
await expectRejects(
73-
client.identifyUser({
74-
verificationId,
75-
}),
76-
{
77-
code: 'session.email_blocklist.email_subaddressing_not_allowed',
78-
status: 422,
79-
}
80-
);
81-
82-
// Should reject the profile creation
83-
await expectRejects(
84-
client.updateProfile({
85-
type: SignInIdentifier.Email,
86-
verificationId,
57+
client.sendVerificationCode({
58+
identifier,
59+
interactionEvent: InteractionEvent.Register,
8760
}),
8861
{
8962
code: 'session.email_blocklist.email_subaddressing_not_allowed',
@@ -104,33 +77,10 @@ devFeatureTest.describe(
10477
interactionEvent: InteractionEvent.Register,
10578
});
10679

107-
const { verificationId, code } = await successfullySendVerificationCode(client, {
108-
identifier,
109-
interactionEvent: InteractionEvent.Register,
110-
});
111-
112-
await successfullyVerifyVerificationCode(client, {
113-
identifier,
114-
verificationId,
115-
code,
116-
});
117-
118-
// Should reject the registration directly
119-
await expectRejects(
120-
client.identifyUser({
121-
verificationId,
122-
}),
123-
{
124-
code: errorCode,
125-
status: 422,
126-
}
127-
);
128-
129-
// Should reject the profile creation
13080
await expectRejects(
131-
client.updateProfile({
132-
type: SignInIdentifier.Email,
133-
verificationId,
81+
client.sendVerificationCode({
82+
identifier,
83+
interactionEvent: InteractionEvent.Register,
13484
}),
13585
{
13686
code: errorCode,

0 commit comments

Comments
 (0)