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

refactor: rename interaction Event -> InteractionEvent (#2718)

This commit is contained in:
Gao Sun 2022-12-26 19:22:56 +08:00 committed by GitHub
parent 5bd2b31eb6
commit 5825620b9b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 188 additions and 182 deletions

View file

@ -1,4 +1,4 @@
import { Event } from '@logto/schemas';
import { InteractionEvent } from '@logto/schemas';
import { HookEvent } from '@logto/schemas/lib/models/hooks.js';
import { mockEsm, mockEsmDefault } from '@logto/shared/esm';
import type { InferModelType } from '@withtyped/server';
@ -61,7 +61,7 @@ describe('triggerInteractionHooksIfNeeded()', () => {
jti: 'some_jti',
result: {
login: { accountId: '123' },
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifier: { connectorId: 'bar' },
},
params: { client_id: 'some_client' },

View file

@ -1,4 +1,4 @@
import { Event, userInfoSelectFields } from '@logto/schemas';
import { InteractionEvent, userInfoSelectFields } from '@logto/schemas';
import { HookEventPayload, HookEvent } from '@logto/schemas/models';
import { trySafe } from '@logto/shared';
import { conditional, pick } from '@silverhand/essentials';
@ -10,10 +10,10 @@ import { findApplicationById } from '#src/queries/application.js';
import { findUserById } from '#src/queries/user.js';
import { getInteractionStorage } from '#src/routes/interaction/utils/interaction.js';
const eventToHook: Record<Event, HookEvent> = {
[Event.Register]: HookEvent.PostRegister,
[Event.SignIn]: HookEvent.PostSignIn,
[Event.ForgotPassword]: HookEvent.PostResetPassword,
const eventToHook: Record<InteractionEvent, HookEvent> = {
[InteractionEvent.Register]: HookEvent.PostRegister,
[InteractionEvent.SignIn]: HookEvent.PostSignIn,
[InteractionEvent.ForgotPassword]: HookEvent.PostResetPassword,
};
export type Interaction = Awaited<ReturnType<Provider['interactionDetails']>>;

View file

@ -1,4 +1,4 @@
import { Event } from '@logto/schemas';
import { InteractionEvent } from '@logto/schemas';
import { mockEsm, pickDefault } from '@logto/shared/esm';
import { createMockLogContext } from '#src/test-utils/koa-audit-log.js';
@ -93,7 +93,7 @@ describe('submit action', () => {
it('register', async () => {
const interaction: VerifiedRegisterInteractionResult = {
event: Event.Register,
event: InteractionEvent.Register,
profile,
identifiers,
};
@ -117,7 +117,7 @@ describe('submit action', () => {
dbEntry: { syncProfile: false },
});
const interaction: VerifiedSignInInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
accountId: 'foo',
profile: { connectorId: 'logto', password: 'password' },
identifiers,
@ -142,7 +142,7 @@ describe('submit action', () => {
it('reset password', async () => {
const interaction: VerifiedForgotPasswordInteractionResult = {
event: Event.ForgotPassword,
event: InteractionEvent.ForgotPassword,
accountId: 'foo',
profile: { password: 'password' },
};

View file

@ -1,5 +1,5 @@
import type { User } from '@logto/schemas';
import { Event } from '@logto/schemas';
import { InteractionEvent } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import type { Context } from 'koa';
import type { Provider } from 'oidc-provider';
@ -91,7 +91,7 @@ export default async function submitInteraction(
) {
const { event, profile } = interaction;
if (event === Event.Register) {
if (event === InteractionEvent.Register) {
const id = await generateUserId();
const upsertProfile = await parseUserProfile(interaction);
@ -107,7 +107,7 @@ export default async function submitInteraction(
const { accountId } = interaction;
if (event === Event.SignIn) {
if (event === InteractionEvent.SignIn) {
const user = await findUserById(accountId);
const upsertProfile = await parseUserProfile(interaction, user);

View file

@ -1,5 +1,5 @@
import { ConnectorType } from '@logto/connector-kit';
import { Event, demoAppApplicationId } from '@logto/schemas';
import { InteractionEvent, demoAppApplicationId } from '@logto/schemas';
import { mockEsmWithActual, mockEsmDefault, mockEsm } from '@logto/shared/esm';
import { mockSignInExperience } from '#src/__mocks__/sign-in-experience.js';
@ -78,7 +78,7 @@ const { storeInteractionResult, mergeIdentifiers, getInteractionStorage } = awai
mergeIdentifiers: jest.fn(),
storeInteractionResult: jest.fn(),
getInteractionStorage: jest.fn().mockResolvedValue({
event: Event.SignIn,
event: InteractionEvent.SignIn,
}),
})
);
@ -129,7 +129,7 @@ describe('session -> interactionRoutes', () => {
it('should call validations properly', async () => {
const body = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifier: { email: 'email@logto.io', password: 'password' },
profile: { phone: '1234567890' },
};
@ -155,10 +155,10 @@ describe('session -> interactionRoutes', () => {
it('should call verifySignInModeSettings properly', async () => {
getInteractionStorage.mockReturnValueOnce({
event: Event.SignIn,
event: InteractionEvent.SignIn,
});
const body = {
event: Event.Register,
event: InteractionEvent.Register,
};
const response = await sessionRequest.put(path).send(body);
@ -170,11 +170,11 @@ describe('session -> interactionRoutes', () => {
it('should reject if switch sign-in event to forgot-password directly', async () => {
getInteractionStorage.mockReturnValueOnce({
event: Event.SignIn,
event: InteractionEvent.SignIn,
});
const body = {
event: Event.ForgotPassword,
event: InteractionEvent.ForgotPassword,
};
const response = await sessionRequest.put(`${interactionPrefix}/event`).send(body);
@ -185,11 +185,11 @@ describe('session -> interactionRoutes', () => {
it('should reject if switch forgot-password to sign-in directly', async () => {
getInteractionStorage.mockReturnValueOnce({
event: Event.ForgotPassword,
event: InteractionEvent.ForgotPassword,
});
const body = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
};
const response = await sessionRequest.put(`${interactionPrefix}/event`).send(body);
@ -248,7 +248,7 @@ describe('session -> interactionRoutes', () => {
it('should call send passcode properly', async () => {
const body = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
email: 'email@logto.io',
};
@ -273,7 +273,7 @@ describe('session -> interactionRoutes', () => {
it('should not call validateMandatoryUserProfile for forgot password request', async () => {
getInteractionStorage.mockReturnValueOnce({
event: Event.ForgotPassword,
event: InteractionEvent.ForgotPassword,
});
await sessionRequest.post(path).send();

View file

@ -1,5 +1,5 @@
import type { LogtoErrorCode } from '@logto/phrases';
import { Event, eventGuard, identifierPayloadGuard, profileGuard } from '@logto/schemas';
import { InteractionEvent, eventGuard, identifierPayloadGuard, profileGuard } from '@logto/schemas';
import type Router from 'koa-router';
import type { Provider } from 'oidc-provider';
import { z } from 'zod';
@ -119,9 +119,9 @@ export default function interactionRoutes<T extends AnonymousRouter>(
// Forgot Password specific event interaction storage can't be shared with other types of interactions
assertThat(
event === Event.ForgotPassword
? interactionStorage.event === Event.ForgotPassword
: interactionStorage.event !== Event.ForgotPassword,
event === InteractionEvent.ForgotPassword
? interactionStorage.event === InteractionEvent.ForgotPassword
: interactionStorage.event !== InteractionEvent.ForgotPassword,
new RequestError({ code: 'session.interaction_not_found', status: 404 })
);
@ -225,7 +225,7 @@ export default function interactionRoutes<T extends AnonymousRouter>(
const verifiedInteraction = await verifyProfile(accountVerifiedInteraction);
if (event !== Event.ForgotPassword) {
if (event !== InteractionEvent.ForgotPassword) {
await validateMandatoryUserProfile(ctx, verifiedInteraction);
}

View file

@ -6,7 +6,7 @@ import {
socialConnectorPayloadGuard,
eventGuard,
profileGuard,
Event,
InteractionEvent,
} from '@logto/schemas';
import { z } from 'zod';
@ -80,13 +80,13 @@ export const anonymousInteractionResultGuard = z.object({
});
export const verifiedRegisterInteractionResultGuard = z.object({
event: z.literal(Event.Register),
event: z.literal(InteractionEvent.Register),
profile: registerProfileSafeGuard,
identifiers: z.array(identifierGuard).optional(),
});
export const verifiedSignInteractionResultGuard = z.object({
event: z.literal(Event.SignIn),
event: z.literal(InteractionEvent.SignIn),
accountId: z.string(),
profile: profileGuard.optional(),
identifiers: z.array(identifierGuard).optional(),
@ -97,7 +97,7 @@ export const forgotPasswordProfileGuard = z.object({
});
export const verifiedForgotPasswordInteractionResultGuard = z.object({
event: z.literal(Event.ForgotPassword),
event: z.literal(InteractionEvent.ForgotPassword),
accountId: z.string(),
profile: forgotPasswordProfileGuard,
});

View file

@ -4,7 +4,7 @@ import type {
EmailPasscodePayload,
PhonePasswordPayload,
PhonePasscodePayload,
Event,
InteractionEvent,
} from '@logto/schemas';
import type { z } from 'zod';
@ -59,15 +59,15 @@ export type ForgotPasswordProfile = z.infer<typeof forgotPasswordProfileGuard>;
export type AnonymousInteractionResult = z.infer<typeof anonymousInteractionResultGuard>;
export type RegisterInteractionResult = Omit<AnonymousInteractionResult, 'event'> & {
event: Event.Register;
event: InteractionEvent.Register;
};
export type SignInInteractionResult = Omit<AnonymousInteractionResult, 'event'> & {
event: Event.SignIn;
event: InteractionEvent.SignIn;
};
export type ForgotPasswordInteractionResult = Omit<AnonymousInteractionResult, 'event'> & {
event: Event.ForgotPassword;
event: InteractionEvent.ForgotPassword;
};
export type AccountVerifiedInteractionResult =

View file

@ -1,6 +1,6 @@
import type { ConnectorSession } from '@logto/connector-kit';
import { connectorSessionGuard } from '@logto/connector-kit';
import type { Event, Profile } from '@logto/schemas';
import type { Profile, InteractionEvent } from '@logto/schemas';
import type { Context } from 'koa';
import type { Provider, InteractionResults } from 'oidc-provider';
import { z } from 'zod';
@ -84,7 +84,7 @@ export const isAccountVerifiedInteractionResult = (
): interaction is AccountVerifiedInteractionResult => Boolean(interaction.accountId);
export const storeInteractionResult = async (
interaction: Omit<AnonymousInteractionResult, 'event'> & { event?: Event },
interaction: Omit<AnonymousInteractionResult, 'event'> & { event?: InteractionEvent },
ctx: Context,
provider: Provider,
merge = false

View file

@ -1,4 +1,4 @@
import { PasscodeType, Event } from '@logto/schemas';
import { PasscodeType, InteractionEvent } from '@logto/schemas';
import { mockEsmWithActual } from '@logto/shared/esm';
import { createMockLogContext } from '#src/test-utils/koa-audit-log.js';
@ -17,27 +17,27 @@ const { sendPasscodeToIdentifier } = await import('./passcode-validation.js');
const sendPasscodeTestCase = [
{
payload: { email: 'email', event: Event.SignIn },
payload: { email: 'email', event: InteractionEvent.SignIn },
createPasscodeParams: [PasscodeType.SignIn, { email: 'email' }],
},
{
payload: { email: 'email', event: Event.Register },
payload: { email: 'email', event: InteractionEvent.Register },
createPasscodeParams: [PasscodeType.Register, { email: 'email' }],
},
{
payload: { email: 'email', event: Event.ForgotPassword },
payload: { email: 'email', event: InteractionEvent.ForgotPassword },
createPasscodeParams: [PasscodeType.ForgotPassword, { email: 'email' }],
},
{
payload: { phone: 'phone', event: Event.SignIn },
payload: { phone: 'phone', event: InteractionEvent.SignIn },
createPasscodeParams: [PasscodeType.SignIn, { phone: 'phone' }],
},
{
payload: { phone: 'phone', event: Event.Register },
payload: { phone: 'phone', event: InteractionEvent.Register },
createPasscodeParams: [PasscodeType.Register, { phone: 'phone' }],
},
{
payload: { phone: 'phone', event: Event.ForgotPassword },
payload: { phone: 'phone', event: InteractionEvent.ForgotPassword },
createPasscodeParams: [PasscodeType.ForgotPassword, { phone: 'phone' }],
},
];

View file

@ -1,4 +1,4 @@
import type { Event } from '@logto/schemas';
import type { InteractionEvent } from '@logto/schemas';
import { PasscodeType } from '@logto/schemas';
import { createPasscode, sendPasscode, verifyPasscode } from '#src/libraries/passcode.js';
@ -10,13 +10,14 @@ import type { SendPasscodePayload, PasscodeIdentifierPayload } from '../types/in
* Refactor Needed:
* This is a work around to map the latest interaction event type to old PasscodeType
* */
const eventToPasscodeTypeMap: Record<Event, PasscodeType> = {
const eventToPasscodeTypeMap: Record<InteractionEvent, PasscodeType> = {
SignIn: PasscodeType.SignIn,
Register: PasscodeType.Register,
ForgotPassword: PasscodeType.ForgotPassword,
};
const getPasscodeTypeByEvent = (event: Event): PasscodeType => eventToPasscodeTypeMap[event];
const getPasscodeTypeByEvent = (event: InteractionEvent): PasscodeType =>
eventToPasscodeTypeMap[event];
export const sendPasscodeToIdentifier = async (
payload: SendPasscodePayload,
@ -36,7 +37,7 @@ export const sendPasscodeToIdentifier = async (
};
export const verifyIdentifierByPasscode = async (
payload: PasscodeIdentifierPayload & { event: Event },
payload: PasscodeIdentifierPayload & { event: InteractionEvent },
jti: string,
createLog: LogContext['createLog']
) => {

View file

@ -1,5 +1,5 @@
import type { SignInExperience } from '@logto/schemas';
import { SignInIdentifier, SignInMode, Event } from '@logto/schemas';
import { SignInIdentifier, SignInMode, InteractionEvent } from '@logto/schemas';
import { mockSignInExperience } from '#src/__mocks__/sign-in-experience.js';
@ -10,19 +10,19 @@ import {
} from './sign-in-experience-validation.js';
describe('verifySignInModeSettings', () => {
it(Event.Register, () => {
it(InteractionEvent.Register, () => {
expect(() => {
verifySignInModeSettings(Event.Register, {
verifySignInModeSettings(InteractionEvent.Register, {
signInMode: SignInMode.SignIn,
} as SignInExperience);
}).toThrow();
expect(() => {
verifySignInModeSettings(Event.Register, {
verifySignInModeSettings(InteractionEvent.Register, {
signInMode: SignInMode.Register,
} as SignInExperience);
}).not.toThrow();
expect(() => {
verifySignInModeSettings(Event.Register, {
verifySignInModeSettings(InteractionEvent.Register, {
signInMode: SignInMode.SignInAndRegister,
} as SignInExperience);
}).not.toThrow();
@ -30,33 +30,35 @@ describe('verifySignInModeSettings', () => {
it('SignIn', () => {
expect(() => {
verifySignInModeSettings(Event.SignIn, { signInMode: SignInMode.SignIn } as SignInExperience);
verifySignInModeSettings(InteractionEvent.SignIn, {
signInMode: SignInMode.SignIn,
} as SignInExperience);
}).not.toThrow();
expect(() => {
verifySignInModeSettings(Event.SignIn, {
verifySignInModeSettings(InteractionEvent.SignIn, {
signInMode: SignInMode.Register,
} as SignInExperience);
}).toThrow();
expect(() => {
verifySignInModeSettings(Event.SignIn, {
verifySignInModeSettings(InteractionEvent.SignIn, {
signInMode: SignInMode.SignInAndRegister,
} as SignInExperience);
}).not.toThrow();
});
it(Event.ForgotPassword, () => {
it(InteractionEvent.ForgotPassword, () => {
expect(() => {
verifySignInModeSettings(Event.ForgotPassword, {
verifySignInModeSettings(InteractionEvent.ForgotPassword, {
signInMode: SignInMode.SignIn,
} as SignInExperience);
}).not.toThrow();
expect(() => {
verifySignInModeSettings(Event.ForgotPassword, {
verifySignInModeSettings(InteractionEvent.ForgotPassword, {
signInMode: SignInMode.Register,
} as SignInExperience);
}).not.toThrow();
expect(() => {
verifySignInModeSettings(Event.ForgotPassword, {
verifySignInModeSettings(InteractionEvent.ForgotPassword, {
signInMode: SignInMode.SignInAndRegister,
} as SignInExperience);
}).not.toThrow();

View file

@ -1,5 +1,5 @@
import type { SignInExperience, Profile, IdentifierPayload } from '@logto/schemas';
import { SignInMode, SignInIdentifier, Event } from '@logto/schemas';
import { SignInMode, SignInIdentifier, InteractionEvent } from '@logto/schemas';
import RequestError from '#src/errors/RequestError/index.js';
import assertThat from '#src/utils/assert-that.js';
@ -11,12 +11,15 @@ const forbiddenIdentifierError = new RequestError({
status: 422,
});
export const verifySignInModeSettings = (event: Event, { signInMode }: SignInExperience) => {
if (event === Event.SignIn) {
export const verifySignInModeSettings = (
event: InteractionEvent,
{ signInMode }: SignInExperience
) => {
if (event === InteractionEvent.SignIn) {
assertThat(signInMode !== SignInMode.Register, forbiddenEventError);
}
if (event === Event.Register) {
if (event === InteractionEvent.Register) {
assertThat(signInMode !== SignInMode.SignIn, forbiddenEventError);
}
};

View file

@ -1,4 +1,4 @@
import { Event } from '@logto/schemas';
import { InteractionEvent } from '@logto/schemas';
import { mockEsm, mockEsmDefault, mockEsmWithActual, pickDefault } from '@logto/shared/esm';
import RequestError from '#src/errors/RequestError/index.js';
@ -36,7 +36,7 @@ const logContext = createMockLogContext();
describe('identifier verification', () => {
const baseCtx = { ...createContextWithRouteParameters(), ...logContext };
const interactionStorage = { event: Event.SignIn };
const interactionStorage = { event: InteractionEvent.SignIn };
afterEach(() => {
jest.clearAllMocks();
@ -174,7 +174,7 @@ describe('identifier verification', () => {
it('verified social email', async () => {
const interactionRecord: AnonymousInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifiers: [
{
key: 'social',
@ -216,7 +216,7 @@ describe('identifier verification', () => {
it('verified social email should throw if social identity not found', async () => {
const interactionRecord: AnonymousInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifiers: [
{
key: 'social',

View file

@ -1,5 +1,5 @@
import type {
Event,
InteractionEvent,
IdentifierPayload,
SocialConnectorPayload,
SocialIdentityPayload,
@ -41,7 +41,7 @@ const verifyPasswordIdentifier = async (
};
const verifyPasscodeIdentifier = async (
event: Event,
event: InteractionEvent,
identifier: PasscodeIdentifierPayload,
ctx: Context,
provider: Provider

View file

@ -1,4 +1,4 @@
import { Event } from '@logto/schemas';
import { InteractionEvent } from '@logto/schemas';
import { mockEsmWithActual, mockEsmDefault, pickDefault } from '@logto/shared/esm';
import { createMockLogContext } from '#src/test-utils/koa-audit-log.js';
@ -29,7 +29,7 @@ describe('verifyIdentifier', () => {
it('should return the interaction record if the event is register', async () => {
const interactionRecord = {
event: Event.Register,
event: InteractionEvent.Register,
};
const result = await verifyIdentifier(ctx, provider, interactionRecord);
@ -41,7 +41,7 @@ describe('verifyIdentifier', () => {
it('should return and assign the verified result to the interaction record if the event is sign in', async () => {
const interactionRecord: SignInInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifiers: [{ key: 'emailVerified', value: 'email@logto.io' }],
};

View file

@ -1,4 +1,4 @@
import { Event } from '@logto/schemas';
import { InteractionEvent } from '@logto/schemas';
import type { Context } from 'koa';
import type { Provider } from 'oidc-provider';
@ -21,7 +21,7 @@ export default async function verifyIdentifier(
provider: Provider,
interactionRecord: InteractionResult
): Promise<RegisterInteractionResult | AccountVerifiedInteractionResult> {
if (interactionRecord.event === Event.Register) {
if (interactionRecord.event === InteractionEvent.Register) {
return interactionRecord;
}

View file

@ -1,4 +1,4 @@
import { Event, MissingProfile, SignInIdentifier } from '@logto/schemas';
import { InteractionEvent, MissingProfile, SignInIdentifier } from '@logto/schemas';
import { mockEsm, mockEsmWithActual, pickDefault } from '@logto/shared/esm';
import type { Provider } from 'oidc-provider';
@ -32,7 +32,7 @@ describe('validateMandatoryUserProfile', () => {
signInExperience: mockSignInExperience,
};
const interaction: IdentifierVerifiedInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
accountId: 'foo',
};

View file

@ -1,5 +1,5 @@
import type { Profile, SignInExperience, User } from '@logto/schemas';
import { Event, MissingProfile, SignInIdentifier } from '@logto/schemas';
import { InteractionEvent, MissingProfile, SignInIdentifier } from '@logto/schemas';
import type { Nullable } from '@silverhand/essentials';
import type { Context } from 'koa';
@ -76,7 +76,7 @@ export default async function validateMandatoryUserProfile(
const { signUp } = ctx.signInExperience;
const { event, accountId, profile } = interaction;
const user = event === Event.Register ? null : await findUserById(accountId);
const user = event === InteractionEvent.Register ? null : await findUserById(accountId);
const missingProfileSet = getMissingProfileBySignUpIdentifiers({ signUp, user, profile });
assertThat(

View file

@ -1,4 +1,4 @@
import { Event } from '@logto/schemas';
import { InteractionEvent } from '@logto/schemas';
import { mockEsm, mockEsmWithActual, pickDefault } from '@logto/shared/esm';
import RequestError from '#src/errors/RequestError/index.js';
@ -17,7 +17,7 @@ const verifyProfile = await pickDefault(import('./profile-verification.js'));
describe('forgot password interaction profile verification', () => {
const baseInteraction = {
event: Event.ForgotPassword,
event: InteractionEvent.ForgotPassword,
accountId: 'foo',
};

View file

@ -1,4 +1,4 @@
import { Event } from '@logto/schemas';
import { InteractionEvent } from '@logto/schemas';
import { mockEsm, mockEsmWithActual, pickDefault } from '@logto/shared/esm';
import RequestError from '#src/errors/RequestError/index.js';
@ -28,7 +28,7 @@ describe('Should throw when providing existing identifiers in profile', () => {
{ key: 'social', connectorId: 'connectorId', userInfo: { id: 'foo' } },
];
const baseInteraction: IdentifierVerifiedInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
accountId: 'foo',
identifiers,
};

View file

@ -1,4 +1,4 @@
import { Event } from '@logto/schemas';
import { InteractionEvent } from '@logto/schemas';
import { mockEsm, mockEsmWithActual, pickDefault } from '@logto/shared/esm';
import RequestError from '#src/errors/RequestError/index.js';
@ -32,7 +32,7 @@ const identifiers: Identifier[] = [
];
const baseInteraction: IdentifierVerifiedInteractionResult = {
event: Event.Register,
event: InteractionEvent.Register,
identifiers,
};

View file

@ -1,4 +1,4 @@
import { Event } from '@logto/schemas';
import { InteractionEvent } from '@logto/schemas';
import { mockEsm, mockEsmWithActual, pickDefault } from '@logto/shared/esm';
import RequestError from '#src/errors/RequestError/index.js';
@ -23,7 +23,7 @@ mockEsm('#src/connectors/index.js', () => ({
const verifyProfile = await pickDefault(import('./profile-verification.js'));
describe('profile protected identifier verification', () => {
const baseInteraction = { event: Event.SignIn, accountId: 'foo' };
const baseInteraction = { event: InteractionEvent.SignIn, accountId: 'foo' };
afterEach(() => {
jest.clearAllMocks();

View file

@ -1,5 +1,5 @@
import type { Profile, User } from '@logto/schemas';
import { Event } from '@logto/schemas';
import { InteractionEvent } from '@logto/schemas';
import { argon2Verify } from 'hash-wasm';
import { getLogtoConnectorById } from '#src/connectors/index.js';
@ -175,7 +175,7 @@ export default async function verifyProfile(
): Promise<VerifiedInteractionResult> {
const { event, identifiers, accountId, profile = {} } = interaction;
if (event === Event.Register) {
if (event === InteractionEvent.Register) {
// Verify the profile includes sufficient identifiers to register a new account
assertThat(
isValidRegisterInteractionResult(interaction),
@ -188,7 +188,7 @@ export default async function verifyProfile(
return interaction;
}
if (event === Event.SignIn) {
if (event === InteractionEvent.SignIn) {
verifyProfileIdentifiers(profile, identifiers);
// Find existing account
const user = await findUserById(accountId);

View file

@ -1,4 +1,4 @@
import { Event } from '@logto/schemas';
import { InteractionEvent } from '@logto/schemas';
import { mockEsm, mockEsmDefault, pickDefault } from '@logto/shared/esm';
import RequestError from '#src/errors/RequestError/index.js';
@ -24,7 +24,7 @@ describe('verifyUserAccount', () => {
it('empty identifiers with accountId', async () => {
const interaction: SignInInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
accountId: 'foo',
};
@ -35,7 +35,7 @@ describe('verifyUserAccount', () => {
it('empty identifiers withOut accountId should throw', async () => {
const interaction: SignInInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
};
await expect(verifyUserAccount(interaction)).rejects.toMatchError(
@ -45,48 +45,48 @@ describe('verifyUserAccount', () => {
it('verify accountId identifier', async () => {
const interaction: SignInInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifiers: [{ key: 'accountId', value: 'foo' }],
};
const result = await verifyUserAccount(interaction);
expect(result).toEqual({ event: Event.SignIn, accountId: 'foo', identifiers: [] });
expect(result).toEqual({ event: InteractionEvent.SignIn, accountId: 'foo', identifiers: [] });
});
it('verify emailVerified identifier', async () => {
findUserByIdentifierMock.mockResolvedValueOnce({ id: 'foo' });
const interaction: SignInInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifiers: [{ key: 'emailVerified', value: 'email' }],
};
const result = await verifyUserAccount(interaction);
expect(findUserByIdentifierMock).toBeCalledWith({ email: 'email' });
expect(result).toEqual({ event: Event.SignIn, accountId: 'foo', identifiers: [] });
expect(result).toEqual({ event: InteractionEvent.SignIn, accountId: 'foo', identifiers: [] });
});
it('verify phoneVerified identifier', async () => {
findUserByIdentifierMock.mockResolvedValueOnce({ id: 'foo' });
const interaction: SignInInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifiers: [{ key: 'phoneVerified', value: '123456' }],
};
const result = await verifyUserAccount(interaction);
expect(findUserByIdentifierMock).toBeCalledWith({ phone: '123456' });
expect(result).toEqual({ event: Event.SignIn, accountId: 'foo', identifiers: [] });
expect(result).toEqual({ event: InteractionEvent.SignIn, accountId: 'foo', identifiers: [] });
});
it('verify social identifier', async () => {
findUserByIdentifierMock.mockResolvedValueOnce({ id: 'foo' });
const interaction: SignInInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifiers: [{ key: 'social', connectorId: 'connectorId', userInfo: { id: 'foo' } }],
};
@ -96,14 +96,14 @@ describe('verifyUserAccount', () => {
userInfo: { id: 'foo' },
});
expect(result).toEqual({ event: Event.SignIn, accountId: 'foo', identifiers: [] });
expect(result).toEqual({ event: InteractionEvent.SignIn, accountId: 'foo', identifiers: [] });
});
it('verify social identifier user identity not exist', async () => {
findUserByIdentifierMock.mockResolvedValueOnce(null);
const interaction: SignInInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifiers: [{ key: 'social', connectorId: 'connectorId', userInfo: { id: 'foo' } }],
};
@ -127,7 +127,7 @@ describe('verifyUserAccount', () => {
findUserByIdentifierMock.mockResolvedValueOnce({ id: 'foo' });
const interaction: SignInInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifiers: [
{ key: 'accountId', value: 'foo' },
{ key: 'emailVerified', value: 'email' },
@ -137,14 +137,14 @@ describe('verifyUserAccount', () => {
const result = await verifyUserAccount(interaction);
expect(findUserByIdentifierMock).toBeCalledWith({ email: 'email' });
expect(result).toEqual({ event: Event.SignIn, accountId: 'foo', identifiers: [] });
expect(result).toEqual({ event: InteractionEvent.SignIn, accountId: 'foo', identifiers: [] });
});
it('verify accountId and emailVerified identifier with email user not exist', async () => {
findUserByIdentifierMock.mockResolvedValueOnce(null);
const interaction: SignInInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifiers: [
{ key: 'accountId', value: 'foo' },
{ key: 'emailVerified', value: 'email' },
@ -164,7 +164,7 @@ describe('verifyUserAccount', () => {
.mockResolvedValueOnce({ id: 'foo2', isSuspended: true });
const interaction: SignInInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifiers: [
{ key: 'emailVerified', value: 'email' },
{ key: 'phoneVerified', value: '123456' },
@ -185,7 +185,7 @@ describe('verifyUserAccount', () => {
.mockResolvedValueOnce({ id: 'foo2' });
const interaction: SignInInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifiers: [
{ key: 'emailVerified', value: 'email' },
{ key: 'phoneVerified', value: '123456' },
@ -203,7 +203,7 @@ describe('verifyUserAccount', () => {
findUserByIdentifierMock.mockResolvedValueOnce({ id: 'foo' });
const interaction: SignInInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
accountId: 'foo2',
identifiers: [{ key: 'emailVerified', value: 'email' }],
};
@ -218,7 +218,7 @@ describe('verifyUserAccount', () => {
findUserByIdentifierMock.mockResolvedValueOnce({ id: 'foo' });
const interaction: SignInInteractionResult = {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifiers: [
{ key: 'social', connectorId: 'connectorId', userInfo: { id: 'foo' } },
{ key: 'emailVerified', value: 'email' },
@ -234,7 +234,7 @@ describe('verifyUserAccount', () => {
expect(findUserByIdentifierMock).toBeCalledWith({ email: 'email' });
expect(result).toEqual({
event: Event.SignIn,
event: InteractionEvent.SignIn,
accountId: 'foo',
identifiers: [
{ key: 'social', connectorId: 'connectorId', userInfo: { id: 'foo' } },

View file

@ -1,4 +1,4 @@
import type { Event, IdentifierPayload, Profile } from '@logto/schemas';
import type { InteractionEvent, IdentifierPayload, Profile } from '@logto/schemas';
import api from './api.js';
@ -7,7 +7,7 @@ export type RedirectResponse = {
};
export type interactionPayload = {
event: Event;
event: InteractionEvent;
identifier?: IdentifierPayload;
profile?: Profile;
};
@ -21,7 +21,7 @@ export const putInteraction = async (cookie: string, payload: interactionPayload
})
.json();
export const putInteractionEvent = async (cookie: string, payload: { event: Event }) =>
export const putInteractionEvent = async (cookie: string, payload: { event: InteractionEvent }) =>
api
.put('interaction/event', { headers: { cookie }, json: payload, followRedirect: false })
.json();
@ -59,10 +59,10 @@ export const submitInteraction = async (cookie: string) =>
export type VerificationPasscodePayload =
| {
event: Event;
event: InteractionEvent;
email: string;
}
| { event: Event; phone: string };
| { event: InteractionEvent; phone: string };
export const sendVerificationPasscode = async (
cookie: string,

View file

@ -1,4 +1,4 @@
import { Event, interaction, SignInIdentifier } from '@logto/schemas';
import { InteractionEvent, interaction, SignInIdentifier } from '@logto/schemas';
import { assert } from '@silverhand/essentials';
import { deleteUser } from '#src/api/admin-user.js';
@ -36,7 +36,7 @@ describe('audit logs for interaction', () => {
const { username, password } = generateNewUserProfile({ username: true, password: true });
await client.send(putInteraction, {
event: Event.Register,
event: InteractionEvent.Register,
profile: { username, password },
});

View file

@ -1,4 +1,4 @@
import { Event, ConnectorType, SignInIdentifier } from '@logto/schemas';
import { InteractionEvent, ConnectorType, SignInIdentifier } from '@logto/schemas';
import {
putInteraction,
@ -39,9 +39,9 @@ describe('reset password', () => {
const client = await initClient();
await client.successSend(putInteraction, { event: Event.ForgotPassword });
await client.successSend(putInteraction, { event: InteractionEvent.ForgotPassword });
await client.successSend(sendVerificationPasscode, {
event: Event.ForgotPassword,
event: InteractionEvent.ForgotPassword,
email: userProfile.primaryEmail,
});
@ -49,7 +49,7 @@ describe('reset password', () => {
expect(passcodeRecord).toMatchObject({
address: userProfile.primaryEmail,
type: Event.ForgotPassword,
type: InteractionEvent.ForgotPassword,
});
const { code } = passcodeRecord;
@ -72,7 +72,7 @@ describe('reset password', () => {
await client.submitInteraction();
await client.successSend(putInteraction, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifier: {
email: userProfile.primaryEmail,
password: newPasscodeRecord,
@ -93,9 +93,9 @@ describe('reset password', () => {
const client = await initClient();
await client.successSend(putInteraction, { event: Event.ForgotPassword });
await client.successSend(putInteraction, { event: InteractionEvent.ForgotPassword });
await client.successSend(sendVerificationPasscode, {
event: Event.ForgotPassword,
event: InteractionEvent.ForgotPassword,
phone: userProfile.primaryPhone,
});
@ -103,7 +103,7 @@ describe('reset password', () => {
expect(passcodeRecord).toMatchObject({
phone: userProfile.primaryPhone,
type: Event.ForgotPassword,
type: InteractionEvent.ForgotPassword,
});
const { code } = passcodeRecord;
@ -126,7 +126,7 @@ describe('reset password', () => {
await client.submitInteraction();
await client.successSend(putInteraction, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifier: {
phone: userProfile.primaryPhone,
password: newPasscodeRecord,

View file

@ -1,4 +1,4 @@
import { ConnectorType, Event, SignInIdentifier } from '@logto/schemas';
import { ConnectorType, InteractionEvent, SignInIdentifier } from '@logto/schemas';
import { assert } from '@silverhand/essentials';
import {
@ -32,7 +32,7 @@ describe('Register with username and password', () => {
const client = await initClient();
await client.send(putInteraction, {
event: Event.Register,
event: InteractionEvent.Register,
profile: {
username,
password,
@ -68,11 +68,11 @@ describe('Register with passwordless identifier', () => {
const client = await initClient();
await client.successSend(putInteraction, {
event: Event.Register,
event: InteractionEvent.Register,
});
await client.successSend(sendVerificationPasscode, {
event: Event.Register,
event: InteractionEvent.Register,
email: primaryEmail,
});
@ -80,7 +80,7 @@ describe('Register with passwordless identifier', () => {
expect(passcodeRecord).toMatchObject({
address: primaryEmail,
type: Event.Register,
type: InteractionEvent.Register,
});
const { code } = passcodeRecord;
@ -112,11 +112,11 @@ describe('Register with passwordless identifier', () => {
const client = await initClient();
await client.successSend(putInteraction, {
event: Event.Register,
event: InteractionEvent.Register,
});
await client.successSend(sendVerificationPasscode, {
event: Event.Register,
event: InteractionEvent.Register,
phone: primaryPhone,
});
@ -124,7 +124,7 @@ describe('Register with passwordless identifier', () => {
expect(passcodeRecord).toMatchObject({
phone: primaryPhone,
type: Event.Register,
type: InteractionEvent.Register,
});
const { code } = passcodeRecord;
@ -160,11 +160,11 @@ describe('Register with passwordless identifier', () => {
const client = await initClient();
await client.successSend(putInteraction, {
event: Event.Register,
event: InteractionEvent.Register,
});
await client.successSend(sendVerificationPasscode, {
event: Event.Register,
event: InteractionEvent.Register,
email: primaryEmail,
});
@ -172,7 +172,7 @@ describe('Register with passwordless identifier', () => {
expect(passcodeRecord).toMatchObject({
address: primaryEmail,
type: Event.Register,
type: InteractionEvent.Register,
});
const { code } = passcodeRecord;
@ -189,7 +189,7 @@ describe('Register with passwordless identifier', () => {
await expectRejects(client.submitInteraction(), 'user.email_already_in_use');
await client.successSend(deleteInteractionProfile);
await client.successSend(putInteractionEvent, { event: Event.SignIn });
await client.successSend(putInteractionEvent, { event: InteractionEvent.SignIn });
const { redirectTo } = await client.submitInteraction();
await processSession(client, redirectTo);
@ -213,11 +213,11 @@ describe('Register with passwordless identifier', () => {
assert(client.interactionCookie, new Error('Session not found'));
await client.successSend(putInteraction, {
event: Event.Register,
event: InteractionEvent.Register,
});
await client.successSend(sendVerificationPasscode, {
event: Event.Register,
event: InteractionEvent.Register,
phone: primaryPhone,
});
@ -225,7 +225,7 @@ describe('Register with passwordless identifier', () => {
expect(passcodeRecord).toMatchObject({
phone: primaryPhone,
type: Event.Register,
type: InteractionEvent.Register,
});
const { code } = passcodeRecord;
@ -242,7 +242,7 @@ describe('Register with passwordless identifier', () => {
await expectRejects(client.submitInteraction(), 'user.phone_already_in_use');
await client.successSend(deleteInteractionProfile);
await client.successSend(putInteractionEvent, { event: Event.SignIn });
await client.successSend(putInteractionEvent, { event: InteractionEvent.SignIn });
const { redirectTo } = await client.submitInteraction();
await processSession(client, redirectTo);

View file

@ -1,4 +1,4 @@
import { ConnectorType, Event, SignInIdentifier } from '@logto/schemas';
import { ConnectorType, InteractionEvent, SignInIdentifier } from '@logto/schemas';
import {
sendVerificationPasscode,
@ -33,11 +33,11 @@ describe('Sign-In flow using passcode identifiers', () => {
const client = await initClient();
await client.successSend(putInteraction, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
});
await client.successSend(sendVerificationPasscode, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
email: userProfile.primaryEmail,
});
@ -45,7 +45,7 @@ describe('Sign-In flow using passcode identifiers', () => {
expect(passcodeRecord).toMatchObject({
address: userProfile.primaryEmail,
type: Event.SignIn,
type: InteractionEvent.SignIn,
});
const { code } = passcodeRecord;
@ -67,11 +67,11 @@ describe('Sign-In flow using passcode identifiers', () => {
const client = await initClient();
await client.successSend(putInteraction, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
});
await client.successSend(sendVerificationPasscode, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
phone: userProfile.primaryPhone,
});
@ -79,7 +79,7 @@ describe('Sign-In flow using passcode identifiers', () => {
expect(passcodeRecord).toMatchObject({
phone: userProfile.primaryPhone,
type: Event.SignIn,
type: InteractionEvent.SignIn,
});
const { code } = passcodeRecord;
@ -107,11 +107,11 @@ describe('Sign-In flow using passcode identifiers', () => {
const client = await initClient();
await client.successSend(putInteraction, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
});
await client.successSend(sendVerificationPasscode, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
email: newEmail,
});
@ -126,7 +126,7 @@ describe('Sign-In flow using passcode identifiers', () => {
await expectRejects(client.submitInteraction(), 'user.user_not_exist');
await client.successSend(putInteractionEvent, { event: Event.Register });
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
await client.successSend(patchInteractionProfile, { email: newEmail });
const { redirectTo } = await client.submitInteraction();
@ -147,11 +147,11 @@ describe('Sign-In flow using passcode identifiers', () => {
const client = await initClient();
await client.successSend(putInteraction, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
});
await client.successSend(sendVerificationPasscode, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
phone: newPhone,
});
@ -166,7 +166,7 @@ describe('Sign-In flow using passcode identifiers', () => {
await expectRejects(client.submitInteraction(), 'user.user_not_exist');
await client.successSend(putInteractionEvent, { event: Event.Register });
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
await client.successSend(patchInteractionProfile, { phone: newPhone });
const { redirectTo } = await client.submitInteraction();

View file

@ -1,4 +1,4 @@
import { Event } from '@logto/schemas';
import { InteractionEvent } from '@logto/schemas';
import { putInteraction, deleteUser } from '#src/api/index.js';
@ -16,7 +16,7 @@ describe('Sign-In flow using password identifiers', () => {
const client = await initClient();
await client.successSend(putInteraction, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifier: {
username: userProfile.username,
password: userProfile.password,
@ -36,7 +36,7 @@ describe('Sign-In flow using password identifiers', () => {
const client = await initClient();
await client.successSend(putInteraction, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifier: {
email: userProfile.primaryEmail,
password: userProfile.password,
@ -56,7 +56,7 @@ describe('Sign-In flow using password identifiers', () => {
const client = await initClient();
await client.successSend(putInteraction, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
identifier: {
phone: userProfile.primaryPhone,
password: userProfile.password,

View file

@ -1,4 +1,4 @@
import { ConnectorType, Event } from '@logto/schemas';
import { ConnectorType, InteractionEvent } from '@logto/schemas';
import { mockSocialConnectorId } from '#src/__mocks__/connectors-mock.js';
import {
@ -48,7 +48,7 @@ describe('Social Identifier Interactions', () => {
const connectorId = connectorIdMap.get(mockSocialConnectorId) ?? '';
await client.successSend(putInteraction, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
});
await client.successSend(createSocialAuthorizationUri, { state, redirectUri, connectorId });
@ -60,7 +60,7 @@ describe('Social Identifier Interactions', () => {
await expectRejects(client.submitInteraction(), 'user.identity_not_exist');
await client.successSend(putInteractionEvent, { event: Event.Register });
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
await client.successSend(patchInteractionProfile, { connectorId });
const { redirectTo } = await client.submitInteraction();
@ -78,7 +78,7 @@ describe('Social Identifier Interactions', () => {
const connectorId = connectorIdMap.get(mockSocialConnectorId) ?? '';
await client.successSend(putInteraction, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
});
await client.successSend(createSocialAuthorizationUri, { state, redirectUri, connectorId });
@ -107,7 +107,7 @@ describe('Social Identifier Interactions', () => {
const connectorId = connectorIdMap.get(mockSocialConnectorId) ?? '';
await client.successSend(putInteraction, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
});
await client.successSend(createSocialAuthorizationUri, { state, redirectUri, connectorId });
@ -133,7 +133,7 @@ describe('Social Identifier Interactions', () => {
const connectorId = connectorIdMap.get(mockSocialConnectorId) ?? '';
await client.successSend(putInteraction, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
});
await client.successSend(createSocialAuthorizationUri, { state, redirectUri, connectorId });
@ -160,7 +160,7 @@ describe('Social Identifier Interactions', () => {
const connectorId = connectorIdMap.get(mockSocialConnectorId) ?? '';
await client.successSend(putInteraction, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
});
await client.successSend(createSocialAuthorizationUri, { state, redirectUri, connectorId });
@ -192,7 +192,7 @@ describe('Social Identifier Interactions', () => {
const connectorId = connectorIdMap.get(mockSocialConnectorId) ?? '';
await client.successSend(putInteraction, {
event: Event.SignIn,
event: InteractionEvent.SignIn,
});
await client.successSend(createSocialAuthorizationUri, { state, redirectUri, connectorId });

View file

@ -52,13 +52,13 @@ export type SocialIdentityPayload = z.infer<typeof socialIdentityPayloadGuard>;
// Interaction Payload Guard
/** Interaction flow (main flow) types. */
export enum Event {
export enum InteractionEvent {
SignIn = 'SignIn',
Register = 'Register',
ForgotPassword = 'ForgotPassword',
}
export const eventGuard = z.nativeEnum(Event);
export const eventGuard = z.nativeEnum(InteractionEvent);
export const identifierPayloadGuard = z.union([
usernamePasswordPayloadGuard,

View file

@ -1,4 +1,4 @@
import type { Event } from '../interactions.js';
import type { InteractionEvent } from '../interactions.js';
export type Prefix = 'Interaction';
@ -42,24 +42,24 @@ export enum Action {
* - Indicates an interaction is started or ended. Normally it is performed by OIDC Provider.
*
* ```ts
* `Interaction.${Event}.${Action.Update | Action.Submit}`
* `Interaction.${InteractionEvent}.${Action.Update | Action.Submit}`
* ```
*
* Since {@link Event} is the primary identifier of interaction type, most of log keys include this info for better query experience.
* Since {@link InteractionEvent} is the primary identifier of interaction type, most of log keys include this info for better query experience.
* The only exception is the initial creation of an interaction, which has a key of `Interaction.Create`,
* since we cannot know the type at that time.
*
* - When {@link Action} is `Update`, it indicates the type of interaction is updating to {@link Event}.
* - When {@link Action} is `Update`, it indicates the type of interaction is updating to {@link InteractionEvent}.
* - When {@link Action} is `Submit`, it indicates the whole interaction is being submitted.
*
* ```ts
* `Interaction.${Event}.${Field.Profile}.${Action.Update}`
* `Interaction.${InteractionEvent}.${Field.Profile}.${Action.Update}`
* ```
*
* - Indicates the profile of an interaction is being updated. It may add or remove profile data.
*
* ```ts
* `Interaction.${Event}.${Field.Identifier}.${Method}.${Action}`
* `Interaction.${InteractionEvent}.${Field.Identifier}.${Method}.${Action}`
* ```
*
* - Indicates an identifier method is being created or submitted to an interaction.
@ -68,12 +68,12 @@ export enum Action {
*/
export type LogKey =
| `${Prefix}.${Action.Create | Action.End}`
| `${Prefix}.${Event}.${Action.Update | Action.Submit}`
| `${Prefix}.${Event}.${Field.Profile}.${Action.Update}`
| `${Prefix}.${Event}.${Field.Identifier}.${Method.VerificationCode}.${
| `${Prefix}.${InteractionEvent}.${Action.Update | Action.Submit}`
| `${Prefix}.${InteractionEvent}.${Field.Profile}.${Action.Update}`
| `${Prefix}.${InteractionEvent}.${Field.Identifier}.${Method.VerificationCode}.${
| Action.Create
| Action.Submit}`
| `${Prefix}.${Event}.${Field.Identifier}.${Exclude<
| `${Prefix}.${InteractionEvent}.${Field.Identifier}.${Exclude<
Method,
Method.VerificationCode
>}.${Action.Submit}`;