0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-13 21:30:30 -05:00

refactor(core): make the interaction event mandatory (#6337)

* refactor(core): refactor backup code generate flow

refactor backup code generate flow

* fix(core): fix api payload

fix api payload

* fix(core): fix rebase issue

fix rebase issue

* refactor(core): make the interaction event mandatory

make the interaction event mandatory

* test: update integration tests

update integration tests

* fix(core): fix the middleware apply bug

fix the koaExperienceInteraction middleware apply bug
This commit is contained in:
simeng-li 2024-07-31 13:43:27 +08:00 committed by GitHub
parent d932e304cb
commit dbc5512c0b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 105 additions and 93 deletions

View file

@ -92,9 +92,12 @@ describe('ExperienceInteraction class', () => {
describe('new user registration', () => { describe('new user registration', () => {
it('First admin user provisioning', async () => { it('First admin user provisioning', async () => {
const experienceInteraction = new ExperienceInteraction(ctx, tenant); const experienceInteraction = new ExperienceInteraction(
ctx,
tenant,
InteractionEvent.Register
);
await experienceInteraction.setInteractionEvent(InteractionEvent.Register);
experienceInteraction.setVerificationRecord(emailVerificationRecord); experienceInteraction.setVerificationRecord(emailVerificationRecord);
await experienceInteraction.identifyUser(emailVerificationRecord.id); await experienceInteraction.identifyUser(emailVerificationRecord.id);

View file

@ -37,7 +37,7 @@ import {
import { VerificationRecordsMap } from './verifications/verification-records-map.js'; import { VerificationRecordsMap } from './verifications/verification-records-map.js';
type InteractionStorage = { type InteractionStorage = {
interactionEvent?: InteractionEvent; interactionEvent: InteractionEvent;
userId?: string; userId?: string;
profile?: InteractionProfile; profile?: InteractionProfile;
mfa?: MfaData; mfa?: MfaData;
@ -45,7 +45,7 @@ type InteractionStorage = {
}; };
const interactionStorageGuard = z.object({ const interactionStorageGuard = z.object({
interactionEvent: z.nativeEnum(InteractionEvent).optional(), interactionEvent: z.nativeEnum(InteractionEvent),
userId: z.string().optional(), userId: z.string().optional(),
profile: interactionProfileGuard.optional(), profile: interactionProfileGuard.optional(),
mfa: mfaDataGuard.optional(), mfa: mfaDataGuard.optional(),
@ -72,18 +72,20 @@ export default class ExperienceInteraction {
private userId?: string; private userId?: string;
private userCache?: User; private userCache?: User;
/** The interaction event for the current interaction. */ /** The interaction event for the current interaction. */
#interactionEvent?: InteractionEvent; #interactionEvent: InteractionEvent;
/** /**
* Create a new `ExperienceInteraction` instance. * Restore experience interaction from the interaction storage.
*
* If the `interactionDetails` is provided, the instance will be initialized with the data from the `interactionDetails` storage.
* Otherwise, a brand new instance will be created.
*/ */
constructor(ctx: WithLogContext, tenant: TenantContext, interactionDetails: Interaction);
/**
* Create a new `ExperienceInteraction` instance.
*/
constructor(ctx: WithLogContext, tenant: TenantContext, interactionEvent: InteractionEvent);
constructor( constructor(
private readonly ctx: WithLogContext, private readonly ctx: WithLogContext,
private readonly tenant: TenantContext, private readonly tenant: TenantContext,
interactionDetails?: Interaction interactionData: Interaction | InteractionEvent
) { ) {
const { libraries, queries } = tenant; const { libraries, queries } = tenant;
@ -96,13 +98,14 @@ export default class ExperienceInteraction {
this.getVerificationRecordByTypeAndId(type, verificationId), this.getVerificationRecordByTypeAndId(type, verificationId),
}; };
if (!interactionDetails) { if (typeof interactionData === 'string') {
this.#interactionEvent = interactionData;
this.profile = new Profile(libraries, queries, {}, interactionContext); this.profile = new Profile(libraries, queries, {}, interactionContext);
this.mfa = new Mfa(libraries, queries, {}, interactionContext); this.mfa = new Mfa(libraries, queries, {}, interactionContext);
return; return;
} }
const result = interactionStorageGuard.safeParse(interactionDetails.result ?? {}); const result = interactionStorageGuard.safeParse(interactionData.result ?? {});
// `interactionDetails.result` is not a valid experience interaction storage // `interactionDetails.result` is not a valid experience interaction storage
assertThat( assertThat(
@ -148,14 +151,12 @@ export default class ExperienceInteraction {
await this.signInExperienceValidator.guardInteractionEvent(interactionEvent); await this.signInExperienceValidator.guardInteractionEvent(interactionEvent);
// `ForgotPassword` interaction event can not interchanged with other events // `ForgotPassword` interaction event can not interchanged with other events
if (this.interactionEvent) { assertThat(
assertThat( interactionEvent === InteractionEvent.ForgotPassword
interactionEvent === InteractionEvent.ForgotPassword ? this.interactionEvent === InteractionEvent.ForgotPassword
? this.interactionEvent === InteractionEvent.ForgotPassword : this.interactionEvent !== InteractionEvent.ForgotPassword,
: this.interactionEvent !== InteractionEvent.ForgotPassword, new RequestError({ code: 'session.not_supported_for_forgot_password', status: 400 })
new RequestError({ code: 'session.not_supported_for_forgot_password', status: 400 }) );
);
}
this.#interactionEvent = interactionEvent; this.#interactionEvent = interactionEvent;
} }

View file

@ -41,7 +41,7 @@ export default function experienceApiRoutes<T extends AnonymousRouter>(
) { ) {
const { queries } = tenant; const { queries } = tenant;
const router = const experienceRouter =
// @ts-expect-error for good koa types // @ts-expect-error for good koa types
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
(anonymousRouter as Router<unknown, WithExperienceInteractionContext<RouterContext<T>>>).use( (anonymousRouter as Router<unknown, WithExperienceInteractionContext<RouterContext<T>>>).use(
@ -49,7 +49,7 @@ export default function experienceApiRoutes<T extends AnonymousRouter>(
koaExperienceInteraction(tenant) koaExperienceInteraction(tenant)
); );
router.put( experienceRouter.put(
experienceRoutes.prefix, experienceRoutes.prefix,
koaGuard({ koaGuard({
body: z.object({ body: z.object({
@ -63,20 +63,19 @@ export default function experienceApiRoutes<T extends AnonymousRouter>(
createLog(`Interaction.${interactionEvent}.Update`); createLog(`Interaction.${interactionEvent}.Update`);
const experienceInteraction = new ExperienceInteraction(ctx, tenant); const experienceInteraction = new ExperienceInteraction(ctx, tenant, interactionEvent);
await experienceInteraction.setInteractionEvent(interactionEvent);
// Save new experience interaction instance.
// This will overwrite any existing interaction data in the storage.
await experienceInteraction.save(); await experienceInteraction.save();
ctx.experienceInteraction = experienceInteraction;
ctx.status = 204; ctx.status = 204;
return next(); return next();
} }
); );
router.put( experienceRouter.put(
`${experienceRoutes.prefix}/interaction-event`, `${experienceRoutes.prefix}/interaction-event`,
koaGuard({ koaGuard({
body: z.object({ body: z.object({
@ -88,9 +87,7 @@ export default function experienceApiRoutes<T extends AnonymousRouter>(
const { interactionEvent } = ctx.guard.body; const { interactionEvent } = ctx.guard.body;
const { createLog, experienceInteraction } = ctx; const { createLog, experienceInteraction } = ctx;
const eventLog = createLog( const eventLog = createLog(`Interaction.${experienceInteraction.interactionEvent}.Update`);
`Interaction.${experienceInteraction.interactionEvent ?? interactionEvent}.Update`
);
await experienceInteraction.setInteractionEvent(interactionEvent); await experienceInteraction.setInteractionEvent(interactionEvent);
@ -106,7 +103,7 @@ export default function experienceApiRoutes<T extends AnonymousRouter>(
} }
); );
router.post( experienceRouter.post(
experienceRoutes.identification, experienceRoutes.identification,
koaGuard({ koaGuard({
body: identificationApiPayloadGuard, body: identificationApiPayloadGuard,
@ -127,7 +124,7 @@ export default function experienceApiRoutes<T extends AnonymousRouter>(
} }
); );
router.post( experienceRouter.post(
`${experienceRoutes.prefix}/submit`, `${experienceRoutes.prefix}/submit`,
koaGuard({ koaGuard({
status: [200, 400, 403, 404, 422], status: [200, 400, 403, 404, 422],
@ -144,14 +141,14 @@ export default function experienceApiRoutes<T extends AnonymousRouter>(
} }
); );
passwordVerificationRoutes(router, tenant); passwordVerificationRoutes(experienceRouter, tenant);
verificationCodeRoutes(router, tenant); verificationCodeRoutes(experienceRouter, tenant);
socialVerificationRoutes(router, tenant); socialVerificationRoutes(experienceRouter, tenant);
enterpriseSsoVerificationRoutes(router, tenant); enterpriseSsoVerificationRoutes(experienceRouter, tenant);
totpVerificationRoutes(router, tenant); totpVerificationRoutes(experienceRouter, tenant);
webAuthnVerificationRoute(router, tenant); webAuthnVerificationRoute(experienceRouter, tenant);
backupCodeVerificationRoutes(router, tenant); backupCodeVerificationRoutes(experienceRouter, tenant);
newPasswordIdentityVerificationRoutes(router, tenant); newPasswordIdentityVerificationRoutes(experienceRouter, tenant);
profileRoutes(router, tenant); profileRoutes(experienceRouter, tenant);
} }

View file

@ -4,6 +4,7 @@ import { type WithLogContext } from '#src/middleware/koa-audit-log.js';
import type TenantContext from '#src/tenants/TenantContext.js'; import type TenantContext from '#src/tenants/TenantContext.js';
import ExperienceInteraction from '../classes/experience-interaction.js'; import ExperienceInteraction from '../classes/experience-interaction.js';
import { experienceRoutes } from '../const.js';
export type WithExperienceInteractionContext<ContextT extends WithLogContext = WithLogContext> = export type WithExperienceInteractionContext<ContextT extends WithLogContext = WithLogContext> =
ContextT & { ContextT & {
@ -25,6 +26,14 @@ export default function koaExperienceInteraction<
tenant: TenantContext tenant: TenantContext
): MiddlewareType<StateT, WithExperienceInteractionContext<ContextT>, ResponseT> { ): MiddlewareType<StateT, WithExperienceInteractionContext<ContextT>, ResponseT> {
return async (ctx, next) => { return async (ctx, next) => {
const { method, path } = ctx.request;
// Should not apply the koaExperienceInteraction middleware to the PUT /experience route.
// New ExperienceInteraction instance are supposed to be created in the PUT /experience route.
if (method === 'PUT' && path === `${experienceRoutes.prefix}`) {
return next();
}
const { provider } = tenant; const { provider } = tenant;
const interactionDetails = await provider.interactionDetails(ctx.req, ctx.res); const interactionDetails = await provider.interactionDetails(ctx.req, ctx.res);

View file

@ -1,4 +1,5 @@
import type { LogtoConfig, SignInOptions } from '@logto/node'; import type { LogtoConfig, SignInOptions } from '@logto/node';
import { InteractionEvent } from '@logto/schemas';
import { assert } from '@silverhand/essentials'; import { assert } from '@silverhand/essentials';
import { ExperienceClient } from '#src/client/experience/index.js'; import { ExperienceClient } from '#src/client/experience/index.js';
@ -17,6 +18,7 @@ export const initClient = async (
}; };
export const initExperienceClient = async ( export const initExperienceClient = async (
interactionEvent: InteractionEvent = InteractionEvent.SignIn,
config?: Partial<LogtoConfig>, config?: Partial<LogtoConfig>,
redirectUri?: string, redirectUri?: string,
options: Omit<SignInOptions, 'redirectUri'> = {} options: Omit<SignInOptions, 'redirectUri'> = {}
@ -24,6 +26,7 @@ export const initExperienceClient = async (
const client = new ExperienceClient(config); const client = new ExperienceClient(config);
await client.initSession(redirectUri, options); await client.initSession(redirectUri, options);
assert(client.interactionCookie, new Error('Session not found')); assert(client.interactionCookie, new Error('Session not found'));
await client.initInteraction({ interactionEvent });
return client; return client;
}; };

View file

@ -34,8 +34,6 @@ export const signInWithPassword = async ({
}) => { }) => {
const client = await initExperienceClient(); const client = await initExperienceClient();
await client.initInteraction({ interactionEvent: InteractionEvent.SignIn });
const { verificationId } = await client.verifyPassword({ const { verificationId } = await client.verifyPassword({
identifier, identifier,
password, password,
@ -54,8 +52,6 @@ export const signInWithPassword = async ({
export const signInWithVerificationCode = async (identifier: VerificationCodeIdentifier) => { export const signInWithVerificationCode = async (identifier: VerificationCodeIdentifier) => {
const client = await initExperienceClient(); const client = await initExperienceClient();
await client.initInteraction({ interactionEvent: InteractionEvent.SignIn });
const { verificationId, code } = await successfullySendVerificationCode(client, { const { verificationId, code } = await successfullySendVerificationCode(client, {
identifier, identifier,
interactionEvent: InteractionEvent.SignIn, interactionEvent: InteractionEvent.SignIn,
@ -89,8 +85,6 @@ export const identifyUserWithUsernamePassword = async (
username: string, username: string,
password: string password: string
) => { ) => {
await client.initInteraction({ interactionEvent: InteractionEvent.SignIn });
const { verificationId } = await client.verifyPassword({ const { verificationId } = await client.verifyPassword({
identifier: { identifier: {
type: SignInIdentifier.Username, type: SignInIdentifier.Username,
@ -108,9 +102,7 @@ export const registerNewUserWithVerificationCode = async (
identifier: VerificationCodeIdentifier, identifier: VerificationCodeIdentifier,
options?: { fulfillPassword?: boolean } options?: { fulfillPassword?: boolean }
) => { ) => {
const client = await initExperienceClient(); const client = await initExperienceClient(InteractionEvent.Register);
await client.initInteraction({ interactionEvent: InteractionEvent.Register });
const { verificationId, code } = await successfullySendVerificationCode(client, { const { verificationId, code } = await successfullySendVerificationCode(client, {
identifier, identifier,
@ -166,7 +158,6 @@ export const signInWithSocial = async (
const redirectUri = 'http://localhost:3000'; const redirectUri = 'http://localhost:3000';
const client = await initExperienceClient(); const client = await initExperienceClient();
await client.initInteraction({ interactionEvent: InteractionEvent.SignIn });
const { verificationId } = await successFullyCreateSocialVerification(client, connectorId, { const { verificationId } = await successFullyCreateSocialVerification(client, connectorId, {
redirectUri, redirectUri,
@ -223,7 +214,6 @@ export const signInWithEnterpriseSso = async (
const redirectUri = 'http://localhost:3000'; const redirectUri = 'http://localhost:3000';
const client = await initExperienceClient(); const client = await initExperienceClient();
await client.initInteraction({ interactionEvent: InteractionEvent.SignIn });
const { verificationId } = await client.getEnterpriseSsoAuthorizationUri(connectorId, { const { verificationId } = await client.getEnterpriseSsoAuthorizationUri(connectorId, {
redirectUri, redirectUri,
@ -257,8 +247,7 @@ export const signInWithEnterpriseSso = async (
}; };
export const registerNewUserUsernamePassword = async (username: string, password: string) => { export const registerNewUserUsernamePassword = async (username: string, password: string) => {
const client = await initExperienceClient(); const client = await initExperienceClient(InteractionEvent.Register);
await client.initInteraction({ interactionEvent: InteractionEvent.Register });
const { verificationId } = await client.createNewPasswordIdentityVerification({ const { verificationId } = await client.createNewPasswordIdentityVerification({
identifier: { identifier: {

View file

@ -43,8 +43,7 @@ devFeatureTest.describe('Bind MFA APIs happy path', () => {
it('should bind TOTP on register', async () => { it('should bind TOTP on register', async () => {
const { username, password } = generateNewUserProfile({ username: true, password: true }); const { username, password } = generateNewUserProfile({ username: true, password: true });
const client = await initExperienceClient(); const client = await initExperienceClient(InteractionEvent.Register);
await client.initInteraction({ interactionEvent: InteractionEvent.Register });
const { verificationId } = await client.createNewPasswordIdentityVerification({ const { verificationId } = await client.createNewPasswordIdentityVerification({
identifier: { identifier: {
@ -131,8 +130,7 @@ devFeatureTest.describe('Bind MFA APIs happy path', () => {
it('should able to skip MFA binding on register', async () => { it('should able to skip MFA binding on register', async () => {
const { username, password } = generateNewUserProfile({ username: true, password: true }); const { username, password } = generateNewUserProfile({ username: true, password: true });
const client = await initExperienceClient(); const client = await initExperienceClient(InteractionEvent.Register);
await client.initInteraction({ interactionEvent: InteractionEvent.Register });
const { verificationId } = await client.createNewPasswordIdentityVerification({ const { verificationId } = await client.createNewPasswordIdentityVerification({
identifier: { identifier: {
@ -193,8 +191,7 @@ devFeatureTest.describe('Bind MFA APIs happy path', () => {
it('should bind TOTP and backup codes on register', async () => { it('should bind TOTP and backup codes on register', async () => {
const { username, password } = generateNewUserProfile({ username: true, password: true }); const { username, password } = generateNewUserProfile({ username: true, password: true });
const client = await initExperienceClient(); const client = await initExperienceClient(InteractionEvent.Register);
await client.initInteraction({ interactionEvent: InteractionEvent.Register });
const { verificationId } = await client.createNewPasswordIdentityVerification({ const { verificationId } = await client.createNewPasswordIdentityVerification({
identifier: { identifier: {

View file

@ -53,8 +53,7 @@ devFeatureTest.describe('Bind MFA APIs sad path', () => {
it('should throw not supported error when binding TOTP on ForgotPassword interaction', async () => { it('should throw not supported error when binding TOTP on ForgotPassword interaction', async () => {
const { username, password } = generateNewUserProfile({ username: true, password: true }); const { username, password } = generateNewUserProfile({ username: true, password: true });
await userApi.create({ username, password }); await userApi.create({ username, password });
const client = await initExperienceClient(); const client = await initExperienceClient(InteractionEvent.ForgotPassword);
await client.initInteraction({ interactionEvent: InteractionEvent.ForgotPassword });
await expectRejects(client.skipMfaBinding(), { await expectRejects(client.skipMfaBinding(), {
code: 'session.not_supported_for_forgot_password', code: 'session.not_supported_for_forgot_password',
@ -69,7 +68,6 @@ devFeatureTest.describe('Bind MFA APIs sad path', () => {
it('should throw identifier_not_found error, if user has not been identified', async () => { it('should throw identifier_not_found error, if user has not been identified', async () => {
const client = await initExperienceClient(); const client = await initExperienceClient();
await client.initInteraction({ interactionEvent: InteractionEvent.SignIn });
await expectRejects(client.bindMfa(MfaFactor.TOTP, 'dummy_verification_id'), { await expectRejects(client.bindMfa(MfaFactor.TOTP, 'dummy_verification_id'), {
code: 'session.identifier_not_found', code: 'session.identifier_not_found',
status: 404, status: 404,

View file

@ -14,10 +14,9 @@ devFeatureTest.describe('PUT /experience API', () => {
it('PUT new experience API should reset all existing verification records', async () => { it('PUT new experience API should reset all existing verification records', async () => {
const { username, password } = generateNewUserProfile({ username: true, password: true }); const { username, password } = generateNewUserProfile({ username: true, password: true });
const user = await userApi.create({ username, password }); await userApi.create({ username, password });
const client = await initExperienceClient(); const client = await initExperienceClient();
await client.initInteraction({ interactionEvent: InteractionEvent.SignIn });
const { verificationId } = await client.verifyPassword({ const { verificationId } = await client.verifyPassword({
identifier: { type: SignInIdentifier.Username, value: username }, identifier: { type: SignInIdentifier.Username, value: username },
password, password,
@ -31,4 +30,36 @@ devFeatureTest.describe('PUT /experience API', () => {
status: 404, status: 404,
}); });
}); });
it('should throw if trying to update interaction event from ForgotPassword to others', async () => {
const client = await initExperienceClient(InteractionEvent.ForgotPassword);
await expectRejects(
client.updateInteractionEvent({ interactionEvent: InteractionEvent.SignIn }),
{
code: 'session.not_supported_for_forgot_password',
status: 400,
}
);
});
it('should throw if trying to update interaction event from SignIn and Register to ForgotPassword', async () => {
const client = await initExperienceClient();
await expectRejects(
client.updateInteractionEvent({ interactionEvent: InteractionEvent.ForgotPassword }),
{
code: 'session.not_supported_for_forgot_password',
status: 400,
}
);
});
it('should update interaction event from SignIn to Register', async () => {
const client = await initExperienceClient();
await expect(
client.updateInteractionEvent({ interactionEvent: InteractionEvent.Register })
).resolves.not.toThrow();
});
}); });

View file

@ -38,9 +38,7 @@ devFeatureTest.describe('Fulfill User Profiles', () => {
}); });
it('should throw 400 if the interaction event is ForgotPassword', async () => { it('should throw 400 if the interaction event is ForgotPassword', async () => {
const client = await initExperienceClient(); const client = await initExperienceClient(InteractionEvent.ForgotPassword);
await client.initInteraction({ interactionEvent: InteractionEvent.ForgotPassword });
await expectRejects( await expectRejects(
client.updateProfile({ type: SignInIdentifier.Username, value: 'username' }), client.updateProfile({ type: SignInIdentifier.Username, value: 'username' }),
@ -54,8 +52,6 @@ devFeatureTest.describe('Fulfill User Profiles', () => {
it('should throw 404 if the interaction is not identified', async () => { it('should throw 404 if the interaction is not identified', async () => {
const client = await initExperienceClient(); const client = await initExperienceClient();
await client.initInteraction({ interactionEvent: InteractionEvent.SignIn });
await expectRejects( await expectRejects(
client.updateProfile({ type: SignInIdentifier.Username, value: 'username' }), client.updateProfile({ type: SignInIdentifier.Username, value: 'username' }),
{ {

View file

@ -14,11 +14,7 @@ import { enableAllPasswordSignInMethods } from '#src/helpers/sign-in-experience.
import { generateNewUserProfile, UserApiTest } from '#src/helpers/user.js'; import { generateNewUserProfile, UserApiTest } from '#src/helpers/user.js';
import { devFeatureTest, generatePassword } from '#src/utils.js'; import { devFeatureTest, generatePassword } from '#src/utils.js';
const initAndIdentifyForgotPasswordInteraction = async ( const identifyForgotPasswordInteraction = async (client: ExperienceClient, email: string) => {
client: ExperienceClient,
email: string
) => {
await client.initInteraction({ interactionEvent: InteractionEvent.ForgotPassword });
const { verificationId, code } = await successfullySendVerificationCode(client, { const { verificationId, code } = await successfullySendVerificationCode(client, {
identifier: { type: SignInIdentifier.Email, value: email }, identifier: { type: SignInIdentifier.Email, value: email },
interactionEvent: InteractionEvent.ForgotPassword, interactionEvent: InteractionEvent.ForgotPassword,
@ -52,8 +48,6 @@ devFeatureTest.describe('Reset Password', () => {
it('should 400 if the interaction is not ForgotPassword', async () => { it('should 400 if the interaction is not ForgotPassword', async () => {
const client = await initExperienceClient(); const client = await initExperienceClient();
await client.initInteraction({ interactionEvent: InteractionEvent.SignIn });
await expectRejects(client.resetPassword({ password: 'password' }), { await expectRejects(client.resetPassword({ password: 'password' }), {
status: 400, status: 400,
code: 'session.invalid_interaction_type', code: 'session.invalid_interaction_type',
@ -61,9 +55,7 @@ devFeatureTest.describe('Reset Password', () => {
}); });
it('should throw 404 if the interaction is not identified', async () => { it('should throw 404 if the interaction is not identified', async () => {
const client = await initExperienceClient(); const client = await initExperienceClient(InteractionEvent.ForgotPassword);
await client.initInteraction({ interactionEvent: InteractionEvent.ForgotPassword });
await expectRejects(client.resetPassword({ password: 'password' }), { await expectRejects(client.resetPassword({ password: 'password' }), {
status: 404, status: 404,
@ -74,8 +66,8 @@ devFeatureTest.describe('Reset Password', () => {
it('should throw 422 if identify the user using VerificationType other than CodeVerification', async () => { it('should throw 422 if identify the user using VerificationType other than CodeVerification', async () => {
const { username, password } = generateNewUserProfile({ username: true, password: true }); const { username, password } = generateNewUserProfile({ username: true, password: true });
await userApi.create({ username, password }); await userApi.create({ username, password });
const client = await initExperienceClient(); const client = await initExperienceClient(InteractionEvent.ForgotPassword);
await client.initInteraction({ interactionEvent: InteractionEvent.ForgotPassword });
const { verificationId } = await client.verifyPassword({ const { verificationId } = await client.verifyPassword({
identifier: { type: SignInIdentifier.Username, value: username }, identifier: { type: SignInIdentifier.Username, value: username },
password, password,
@ -93,9 +85,9 @@ devFeatureTest.describe('Reset Password', () => {
password: true, password: true,
}); });
await userApi.create({ primaryEmail, password }); await userApi.create({ primaryEmail, password });
const client = await initExperienceClient(); const client = await initExperienceClient(InteractionEvent.ForgotPassword);
await initAndIdentifyForgotPasswordInteraction(client, primaryEmail); await identifyForgotPasswordInteraction(client, primaryEmail);
await expectRejects(client.resetPassword({ password }), { await expectRejects(client.resetPassword({ password }), {
status: 422, status: 422,
@ -123,9 +115,9 @@ devFeatureTest.describe('Reset Password', () => {
await userApi.create({ primaryEmail, password }); await userApi.create({ primaryEmail, password });
const client = await initExperienceClient(); const client = await initExperienceClient(InteractionEvent.ForgotPassword);
await initAndIdentifyForgotPasswordInteraction(client, primaryEmail); await identifyForgotPasswordInteraction(client, primaryEmail);
await expectRejects(client.resetPassword({ password: primaryEmail }), { await expectRejects(client.resetPassword({ password: primaryEmail }), {
status: 422, status: 422,
@ -142,9 +134,9 @@ devFeatureTest.describe('Reset Password', () => {
const newPassword = generatePassword(); const newPassword = generatePassword();
const client = await initExperienceClient(); const client = await initExperienceClient(InteractionEvent.ForgotPassword);
await initAndIdentifyForgotPasswordInteraction(client, primaryEmail); await identifyForgotPasswordInteraction(client, primaryEmail);
await client.resetPassword({ password: newPassword }); await client.resetPassword({ password: newPassword });

View file

@ -59,8 +59,7 @@ devFeatureTest.describe('Register interaction with verification code happy path'
value: userProfile[identifiersTypeToUserProfile[identifierType]]!, value: userProfile[identifiersTypeToUserProfile[identifierType]]!,
}; };
const client = await initExperienceClient(); const client = await initExperienceClient(InteractionEvent.Register);
await client.initInteraction({ interactionEvent: InteractionEvent.Register });
const { verificationId, code } = await successfullySendVerificationCode(client, { const { verificationId, code } = await successfullySendVerificationCode(client, {
identifier, identifier,

View file

@ -137,8 +137,6 @@ devFeatureTest.describe('social sign-in and sign-up', () => {
}); });
const client = await initExperienceClient(); const client = await initExperienceClient();
await client.initInteraction({ interactionEvent: InteractionEvent.SignIn });
const { verificationId } = await successFullyCreateSocialVerification(client, connectorId, { const { verificationId } = await successFullyCreateSocialVerification(client, connectorId, {
redirectUri, redirectUri,
state, state,

View file

@ -61,7 +61,6 @@ devFeatureTest.describe('Sign-in with verification code', () => {
}; };
const client = await initExperienceClient(); const client = await initExperienceClient();
await client.initInteraction({ interactionEvent: InteractionEvent.SignIn });
const { verificationId, code } = await successfullySendVerificationCode(client, { const { verificationId, code } = await successfullySendVerificationCode(client, {
identifier, identifier,