mirror of
https://github.com/logto-io/logto.git
synced 2025-04-14 23:11:31 -05:00
feat(core): guard captcha before creating users (#7189)
This commit is contained in:
parent
dc13cc73dd
commit
1baaaef19b
19 changed files with 246 additions and 134 deletions
|
@ -298,6 +298,7 @@ export default class ExperienceInteraction {
|
|||
await this.save();
|
||||
}
|
||||
|
||||
await this.guardCaptcha();
|
||||
await this.profile.assertUserMandatoryProfileFulfilled();
|
||||
|
||||
const user = await this.provisionLibrary.createUser(this.profile.data);
|
||||
|
@ -428,10 +429,7 @@ export default class ExperienceInteraction {
|
|||
queries: { users: userQueries, userSsoIdentities: userSsoIdentityQueries },
|
||||
} = this.tenant;
|
||||
|
||||
if (EnvSet.values.isDevFeaturesEnabled && !this.captcha.verified && !this.captcha.skipped) {
|
||||
// Check if the captcha is required for the current interaction
|
||||
await this.signInExperienceValidator.guardCaptcha(this.interactionEvent);
|
||||
}
|
||||
await this.guardCaptcha();
|
||||
|
||||
// Identified
|
||||
const user = await this.getIdentifiedUser();
|
||||
|
@ -607,6 +605,18 @@ export default class ExperienceInteraction {
|
|||
return Boolean(ssoVerificationRecord?.isVerified);
|
||||
}
|
||||
|
||||
private async guardCaptcha() {
|
||||
if (!EnvSet.values.isDevFeaturesEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.captcha.verified || this.captcha.skipped) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.signInExperienceValidator.guardCaptcha();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the interaction storage.
|
||||
*/
|
||||
|
|
|
@ -240,24 +240,14 @@ export class SignInExperienceValidator {
|
|||
|
||||
/**
|
||||
* Guard the captcha required based on the captcha policy.
|
||||
* Only call this method if captcha is not verified.
|
||||
*
|
||||
* @param event The interaction event.
|
||||
* Only call this method if captcha is not verified or skipped.
|
||||
*
|
||||
* @throws {RequestError} with 422 if the captcha is required
|
||||
*/
|
||||
public async guardCaptcha(event: InteractionEvent) {
|
||||
public async guardCaptcha() {
|
||||
const { captchaPolicy } = await this.getSignInExperienceData();
|
||||
|
||||
if (event === InteractionEvent.SignIn && !captchaPolicy.signIn) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event === InteractionEvent.Register && !captchaPolicy.signUp) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event === InteractionEvent.ForgotPassword && !captchaPolicy.forgotPassword) {
|
||||
if (!captchaPolicy.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -80,13 +80,12 @@ export const putInteraction = async (cookie: string, payload: InteractionPayload
|
|||
.json();
|
||||
|
||||
export const initAdminExperienceClient = async (config?: Partial<LogtoConfig>) =>
|
||||
initExperienceClient(
|
||||
InteractionEvent.SignIn,
|
||||
{ endpoint: logtoConsoleUrl, appId: adminConsoleApplicationId, ...config },
|
||||
adminConsoleRedirectUri,
|
||||
undefined,
|
||||
adminTenantApi
|
||||
);
|
||||
initExperienceClient({
|
||||
interactionEvent: InteractionEvent.SignIn,
|
||||
config: { endpoint: logtoConsoleUrl, appId: adminConsoleApplicationId, ...config },
|
||||
redirectUri: adminConsoleRedirectUri,
|
||||
api: adminTenantApi,
|
||||
});
|
||||
|
||||
export const initClientAndSignIn = async (
|
||||
username: string,
|
||||
|
|
|
@ -18,14 +18,21 @@ export const initClient = async (
|
|||
return client;
|
||||
};
|
||||
|
||||
export const initExperienceClient = async (
|
||||
interactionEvent: InteractionEvent = InteractionEvent.SignIn,
|
||||
config?: Partial<LogtoConfig>,
|
||||
redirectUri?: string,
|
||||
options: Omit<SignInOptions, 'redirectUri'> = {},
|
||||
api?: KyInstance,
|
||||
captchaToken?: string
|
||||
) => {
|
||||
export const initExperienceClient = async ({
|
||||
interactionEvent = InteractionEvent.SignIn,
|
||||
config,
|
||||
redirectUri,
|
||||
options = {},
|
||||
api,
|
||||
captchaToken,
|
||||
}: {
|
||||
interactionEvent?: InteractionEvent;
|
||||
config?: Partial<LogtoConfig>;
|
||||
redirectUri?: string;
|
||||
options?: Omit<SignInOptions, 'redirectUri'>;
|
||||
api?: KyInstance;
|
||||
captchaToken?: string;
|
||||
} = {}) => {
|
||||
const client = new ExperienceClient(config, api);
|
||||
await client.initSession(redirectUri, options);
|
||||
assert(client.interactionCookie, new Error('Session not found'));
|
||||
|
|
|
@ -34,14 +34,9 @@ export const signInWithPassword = async ({
|
|||
password: string;
|
||||
captchaToken?: string;
|
||||
}) => {
|
||||
const client = await initExperienceClient(
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
captchaToken
|
||||
);
|
||||
const client = await initExperienceClient({
|
||||
captchaToken,
|
||||
});
|
||||
|
||||
const { verificationId } = await client.verifyPassword({
|
||||
identifier,
|
||||
|
@ -111,7 +106,9 @@ export const registerNewUserWithVerificationCode = async (
|
|||
identifier: VerificationCodeIdentifier,
|
||||
options?: { fulfillPassword?: boolean }
|
||||
) => {
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
const { verificationId, code } = await successfullySendVerificationCode(client, {
|
||||
identifier,
|
||||
|
@ -268,8 +265,15 @@ export const signInWithEnterpriseSso = async (
|
|||
return userId;
|
||||
};
|
||||
|
||||
export const registerNewUserUsernamePassword = async (username: string, password: string) => {
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
export const registerNewUserUsernamePassword = async (
|
||||
username: string,
|
||||
password: string,
|
||||
captchaToken?: string
|
||||
) => {
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
captchaToken,
|
||||
});
|
||||
|
||||
const { verificationId } = await client.createNewPasswordIdentityVerification({
|
||||
identifier: {
|
||||
|
|
|
@ -202,17 +202,13 @@ export const setLanguage = async (
|
|||
export const enableCaptcha = async () =>
|
||||
updateSignInExperience({
|
||||
captchaPolicy: {
|
||||
signIn: true,
|
||||
signUp: true,
|
||||
forgotPassword: true,
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
|
||||
export const disableCaptcha = async () =>
|
||||
updateSignInExperience({
|
||||
captchaPolicy: {
|
||||
signIn: false,
|
||||
signUp: false,
|
||||
forgotPassword: false,
|
||||
enabled: false,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -39,9 +39,12 @@ describe('experience API with i18n email templates', () => {
|
|||
name: 'test org',
|
||||
});
|
||||
|
||||
const client = await initExperienceClient(undefined, undefined, undefined, {
|
||||
extraParams: {
|
||||
organization_id: organization.id,
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.SignIn,
|
||||
options: {
|
||||
extraParams: {
|
||||
organization_id: organization.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -77,7 +80,9 @@ describe('experience API with i18n email templates', () => {
|
|||
({ usageType }) => usageType === 'ForgotPassword'
|
||||
)!;
|
||||
|
||||
const client = await initExperienceClient();
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.ForgotPassword,
|
||||
});
|
||||
const { code } = await successfullySendVerificationCode(client, {
|
||||
interactionEvent: InteractionEvent.ForgotPassword,
|
||||
identifier: {
|
||||
|
|
|
@ -44,7 +44,9 @@ describe('Bind MFA APIs happy path', () => {
|
|||
|
||||
it('should bind TOTP on register', async () => {
|
||||
const { username, password } = generateNewUserProfile({ username: true, password: true });
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
const { verificationId } = await client.createNewPasswordIdentityVerification({
|
||||
identifier: {
|
||||
|
@ -131,7 +133,9 @@ describe('Bind MFA APIs happy path', () => {
|
|||
|
||||
it('should able to skip MFA binding on register', async () => {
|
||||
const { username, password } = generateNewUserProfile({ username: true, password: true });
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
const { verificationId } = await client.createNewPasswordIdentityVerification({
|
||||
identifier: {
|
||||
|
@ -192,7 +196,9 @@ describe('Bind MFA APIs happy path', () => {
|
|||
|
||||
it('should able to register without MFA', async () => {
|
||||
const { username, password } = generateNewUserProfile({ username: true, password: true });
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
const { verificationId } = await client.createNewPasswordIdentityVerification({
|
||||
identifier: {
|
||||
|
@ -236,7 +242,9 @@ describe('Bind MFA APIs happy path', () => {
|
|||
|
||||
it('should able to register without MFA', async () => {
|
||||
const { username, password } = generateNewUserProfile({ username: true, password: true });
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
const { verificationId } = await client.createNewPasswordIdentityVerification({
|
||||
identifier: {
|
||||
|
@ -272,7 +280,9 @@ describe('Bind MFA APIs happy path', () => {
|
|||
|
||||
it('should bind TOTP and backup codes on register', async () => {
|
||||
const { username, password } = generateNewUserProfile({ username: true, password: true });
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
const { verificationId } = await client.createNewPasswordIdentityVerification({
|
||||
identifier: {
|
||||
|
|
|
@ -90,7 +90,9 @@ describe('Organization required MFA policy', () => {
|
|||
const { username, password } = generateNewUserProfile({ username: true, password: true });
|
||||
|
||||
// Register user and skip MFA
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
const { verificationId } = await client.createNewPasswordIdentityVerification({
|
||||
identifier: {
|
||||
type: SignInIdentifier.Username,
|
||||
|
|
|
@ -52,7 +52,9 @@ describe('Bind MFA APIs sad path', () => {
|
|||
it('should throw not supported error when binding TOTP on ForgotPassword interaction', async () => {
|
||||
const { username, password } = generateNewUserProfile({ username: true, password: true });
|
||||
await userApi.create({ username, password });
|
||||
const client = await initExperienceClient(InteractionEvent.ForgotPassword);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.ForgotPassword,
|
||||
});
|
||||
|
||||
await expectRejects(client.skipMfaBinding(), {
|
||||
code: 'session.not_supported_for_forgot_password',
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
import { SignInIdentifier } from '@logto/schemas';
|
||||
|
||||
import { deleteUser } from '#src/api/admin-user.js';
|
||||
import { updateSignInExperience } from '#src/api/sign-in-experience.js';
|
||||
import { setAlwaysFailCaptcha, setAlwaysPassCaptcha } from '#src/helpers/captcha.js';
|
||||
import { signInWithPassword } from '#src/helpers/experience/index.js';
|
||||
import {
|
||||
registerNewUserUsernamePassword,
|
||||
signInWithPassword,
|
||||
} from '#src/helpers/experience/index.js';
|
||||
import { expectRejects } from '#src/helpers/index.js';
|
||||
import {
|
||||
disableCaptcha,
|
||||
enableAllPasswordSignInMethods,
|
||||
enableCaptcha,
|
||||
} from '#src/helpers/sign-in-experience.js';
|
||||
import { generateNewUser } from '#src/helpers/user.js';
|
||||
import { generateNewUser, generateNewUserProfile } from '#src/helpers/user.js';
|
||||
import { devFeatureTest } from '#src/utils.js';
|
||||
|
||||
const { describe, it } = devFeatureTest;
|
||||
|
@ -17,75 +21,118 @@ const { describe, it } = devFeatureTest;
|
|||
describe('captcha', () => {
|
||||
beforeAll(async () => {
|
||||
await enableAllPasswordSignInMethods();
|
||||
await updateSignInExperience({
|
||||
signUp: {
|
||||
identifiers: [SignInIdentifier.Username],
|
||||
password: true,
|
||||
verify: false,
|
||||
},
|
||||
passwordPolicy: {},
|
||||
});
|
||||
await enableCaptcha();
|
||||
await setAlwaysPassCaptcha();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await setAlwaysPassCaptcha();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await disableCaptcha();
|
||||
});
|
||||
|
||||
it('should sign-in successfully with captcha token', async () => {
|
||||
await setAlwaysPassCaptcha();
|
||||
const { userProfile, user } = await generateNewUser({
|
||||
username: true,
|
||||
password: true,
|
||||
});
|
||||
describe('basic sign in and captcha verification failure', () => {
|
||||
it('should sign-in successfully with captcha token', async () => {
|
||||
const { userProfile, user } = await generateNewUser({
|
||||
username: true,
|
||||
password: true,
|
||||
});
|
||||
|
||||
await signInWithPassword({
|
||||
identifier: {
|
||||
type: SignInIdentifier.Username,
|
||||
value: userProfile.username,
|
||||
},
|
||||
password: userProfile.password,
|
||||
captchaToken: 'captcha-token',
|
||||
});
|
||||
|
||||
await deleteUser(user.id);
|
||||
});
|
||||
|
||||
it('should fail to sign-in if no captcha token', async () => {
|
||||
const { userProfile, user } = await generateNewUser({
|
||||
username: true,
|
||||
password: true,
|
||||
});
|
||||
|
||||
await expectRejects(
|
||||
signInWithPassword({
|
||||
await signInWithPassword({
|
||||
identifier: {
|
||||
type: SignInIdentifier.Username,
|
||||
value: userProfile.username,
|
||||
},
|
||||
password: userProfile.password,
|
||||
}),
|
||||
{
|
||||
code: 'session.captcha_required',
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
captchaToken: 'captcha-token',
|
||||
});
|
||||
|
||||
await deleteUser(user.id);
|
||||
});
|
||||
|
||||
it('should fail to sign-in if captcha token is invalid', async () => {
|
||||
await setAlwaysFailCaptcha();
|
||||
const { userProfile, user } = await generateNewUser({
|
||||
username: true,
|
||||
password: true,
|
||||
await deleteUser(user.id);
|
||||
});
|
||||
|
||||
await expectRejects(
|
||||
signInWithPassword({
|
||||
identifier: { type: SignInIdentifier.Username, value: userProfile.username },
|
||||
password: userProfile.password,
|
||||
captchaToken: 'captcha-token',
|
||||
}),
|
||||
{
|
||||
code: 'session.captcha_failed',
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
it('should fail to sign-in if no captcha token', async () => {
|
||||
const { userProfile, user } = await generateNewUser({
|
||||
username: true,
|
||||
password: true,
|
||||
});
|
||||
|
||||
await deleteUser(user.id);
|
||||
await expectRejects(
|
||||
signInWithPassword({
|
||||
identifier: {
|
||||
type: SignInIdentifier.Username,
|
||||
value: userProfile.username,
|
||||
},
|
||||
password: userProfile.password,
|
||||
}),
|
||||
{
|
||||
code: 'session.captcha_required',
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
await deleteUser(user.id);
|
||||
});
|
||||
|
||||
it('should fail to sign-in if captcha token is invalid', async () => {
|
||||
await setAlwaysFailCaptcha();
|
||||
const { userProfile, user } = await generateNewUser({
|
||||
username: true,
|
||||
password: true,
|
||||
});
|
||||
|
||||
await expectRejects(
|
||||
signInWithPassword({
|
||||
identifier: { type: SignInIdentifier.Username, value: userProfile.username },
|
||||
password: userProfile.password,
|
||||
captchaToken: 'captcha-token',
|
||||
}),
|
||||
{
|
||||
code: 'session.captcha_failed',
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
await deleteUser(user.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('register', () => {
|
||||
it('should register successfully with captcha token', async () => {
|
||||
const { username, password } = generateNewUserProfile({ username: true, password: true });
|
||||
const userId = await registerNewUserUsernamePassword(username, password, 'captcha-token');
|
||||
|
||||
await signInWithPassword({
|
||||
identifier: {
|
||||
type: SignInIdentifier.Username,
|
||||
value: username,
|
||||
},
|
||||
password,
|
||||
captchaToken: 'captcha-token',
|
||||
});
|
||||
|
||||
await deleteUser(userId);
|
||||
});
|
||||
|
||||
it('should fail to register if no captcha token is provided', async () => {
|
||||
const { username, password } = generateNewUserProfile({ username: true, password: true });
|
||||
await expectRejects(registerNewUserUsernamePassword(username, password), {
|
||||
code: 'session.captcha_required',
|
||||
status: 422,
|
||||
});
|
||||
|
||||
// Register again with the same username, ensure the user is not created
|
||||
const userId = await registerNewUserUsernamePassword(username, password, 'captcha-token');
|
||||
await deleteUser(userId);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -31,7 +31,9 @@ describe('PUT /experience API', () => {
|
|||
});
|
||||
|
||||
it('should throw if trying to update interaction event from ForgotPassword to others', async () => {
|
||||
const client = await initExperienceClient(InteractionEvent.ForgotPassword);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.ForgotPassword,
|
||||
});
|
||||
|
||||
await expectRejects(
|
||||
client.updateInteractionEvent({ interactionEvent: InteractionEvent.SignIn }),
|
||||
|
|
|
@ -38,7 +38,9 @@ describe('Fulfill User Profiles', () => {
|
|||
});
|
||||
|
||||
it('should throw 400 if the interaction event is ForgotPassword', async () => {
|
||||
const client = await initExperienceClient(InteractionEvent.ForgotPassword);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.ForgotPassword,
|
||||
});
|
||||
|
||||
await expectRejects(
|
||||
client.updateProfile({ type: SignInIdentifier.Username, value: 'username' }),
|
||||
|
|
|
@ -55,7 +55,9 @@ describe('Reset Password', () => {
|
|||
});
|
||||
|
||||
it('should throw 404 if the interaction is not identified', async () => {
|
||||
const client = await initExperienceClient(InteractionEvent.ForgotPassword);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.ForgotPassword,
|
||||
});
|
||||
|
||||
await expectRejects(client.resetPassword({ password: 'password' }), {
|
||||
status: 404,
|
||||
|
@ -66,7 +68,9 @@ describe('Reset Password', () => {
|
|||
it('should throw 422 if identify the user using VerificationType other than CodeVerification', async () => {
|
||||
const { username, password } = generateNewUserProfile({ username: true, password: true });
|
||||
await userApi.create({ username, password });
|
||||
const client = await initExperienceClient(InteractionEvent.ForgotPassword);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.ForgotPassword,
|
||||
});
|
||||
|
||||
const { verificationId } = await client.verifyPassword({
|
||||
identifier: { type: SignInIdentifier.Username, value: username },
|
||||
|
@ -85,7 +89,9 @@ describe('Reset Password', () => {
|
|||
password: true,
|
||||
});
|
||||
await userApi.create({ primaryEmail, password });
|
||||
const client = await initExperienceClient(InteractionEvent.ForgotPassword);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.ForgotPassword,
|
||||
});
|
||||
|
||||
await identifyForgotPasswordInteraction(client, primaryEmail);
|
||||
|
||||
|
@ -115,7 +121,9 @@ describe('Reset Password', () => {
|
|||
|
||||
await userApi.create({ primaryEmail, password });
|
||||
|
||||
const client = await initExperienceClient(InteractionEvent.ForgotPassword);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.ForgotPassword,
|
||||
});
|
||||
|
||||
await identifyForgotPasswordInteraction(client, primaryEmail);
|
||||
|
||||
|
@ -134,7 +142,9 @@ describe('Reset Password', () => {
|
|||
|
||||
const newPassword = generatePassword();
|
||||
|
||||
const client = await initExperienceClient(InteractionEvent.ForgotPassword);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.ForgotPassword,
|
||||
});
|
||||
|
||||
await identifyForgotPasswordInteraction(client, primaryEmail);
|
||||
|
||||
|
|
|
@ -42,7 +42,9 @@ describe('should reject the email registration if the email domain is enabled fo
|
|||
});
|
||||
|
||||
it('should block email verification code registration', async () => {
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
const { verificationId, code } = await successfullySendVerificationCode(client, {
|
||||
identifier,
|
||||
|
@ -67,7 +69,9 @@ describe('should reject the email registration if the email domain is enabled fo
|
|||
});
|
||||
|
||||
it('should block email profile update', async () => {
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
const { verificationId, code } = await successfullySendVerificationCode(client, {
|
||||
identifier,
|
||||
|
@ -110,7 +114,9 @@ describe('should reject the email registration if the email domain is enabled fo
|
|||
|
||||
it('should block social register with SSO only email identifier', async () => {
|
||||
const connectorId = connectorIdMap.get(mockSocialConnectorId)!;
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
const { verificationId } = await successFullyCreateSocialVerification(client, connectorId, {
|
||||
redirectUri,
|
||||
|
@ -141,7 +147,9 @@ describe('should reject the email registration if the email domain is enabled fo
|
|||
|
||||
it('should block social link email with SSO only email identifier', async () => {
|
||||
const connectorId = connectorIdMap.get(mockSocialConnectorId)!;
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
const { verificationId } = await successFullyCreateSocialVerification(client, connectorId, {
|
||||
redirectUri,
|
||||
|
|
|
@ -40,7 +40,9 @@ describe('Register interaction with one-time token happy path', () => {
|
|||
});
|
||||
|
||||
it('should successfully register a new user with a magic link containing a one-time token', async () => {
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
const oneTimeToken = await createOneTimeToken({
|
||||
email: 'foo@logto.io',
|
||||
|
@ -72,7 +74,9 @@ describe('Register interaction with one-time token happy path', () => {
|
|||
email: userProfile.primaryEmail,
|
||||
});
|
||||
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
const { verificationId } = await client.verifyOneTimeToken({
|
||||
token: oneTimeToken.token,
|
||||
|
@ -111,7 +115,9 @@ describe('Register interaction with one-time token happy path', () => {
|
|||
},
|
||||
});
|
||||
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
const oneTimeToken = await createOneTimeToken({
|
||||
email: 'bar@logto.io',
|
||||
|
|
|
@ -52,7 +52,9 @@ describe('register new user with username and password', () => {
|
|||
const existUsername = generateUsername();
|
||||
await userApi.create({ username: existUsername });
|
||||
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
await expectRejects(
|
||||
client.updateProfile({ type: SignInIdentifier.Username, value: existUsername }),
|
||||
|
@ -117,7 +119,9 @@ devFeatureTest.describe(
|
|||
primaryEmail: true,
|
||||
});
|
||||
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
await client.updateProfile({ type: SignInIdentifier.Username, value: username });
|
||||
await client.updateProfile({ type: 'password', value: password });
|
||||
|
|
|
@ -74,7 +74,9 @@ describe('Register interaction with verification code happy path', () => {
|
|||
value: userProfile[identifiersTypeToUserProfile[identifierType]]!,
|
||||
};
|
||||
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
const { verificationId, code } = await successfullySendVerificationCode(client, {
|
||||
identifier,
|
||||
|
@ -136,7 +138,9 @@ describe('Register interaction with verification code happy path', () => {
|
|||
value: userProfile[identifiersTypeToUserProfile[identifierType]]!,
|
||||
};
|
||||
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
|
||||
const { verificationId, code } = await successfullySendVerificationCode(client, {
|
||||
identifier,
|
||||
|
@ -218,7 +222,9 @@ devFeatureTest.describe('username as secondary identifier', () => {
|
|||
value: identifierType === SignInIdentifier.Email ? generateEmail() : generatePhone(),
|
||||
};
|
||||
|
||||
const client = await initExperienceClient(InteractionEvent.Register);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
});
|
||||
const { verificationId, code } = await successfullySendVerificationCode(client, {
|
||||
identifier,
|
||||
interactionEvent: InteractionEvent.Register,
|
||||
|
|
|
@ -248,7 +248,9 @@ describe('experience api hook trigger', () => {
|
|||
const dataHook = webHookApi.hooks.get('dataHookEventListener')!;
|
||||
const user = userApi.users.find(({ username: name }) => name === username)!;
|
||||
|
||||
const client = await initExperienceClient(InteractionEvent.ForgotPassword);
|
||||
const client = await initExperienceClient({
|
||||
interactionEvent: InteractionEvent.ForgotPassword,
|
||||
});
|
||||
await identifyUserWithEmailVerificationCode(client, email);
|
||||
await client.resetPassword({ password: newPassword });
|
||||
await client.submitInteraction();
|
||||
|
|
Loading…
Add table
Reference in a new issue