From 1cf66e8ad382a31775307ab11bbaced43866fb19 Mon Sep 17 00:00:00 2001 From: Vatsal Goel <144617902+VatsalGoel3@users.noreply.github.com> Date: Sun, 16 Mar 2025 22:14:32 -0600 Subject: [PATCH 1/7] feat(parser): add Cognito pre-signup trigger schema --- packages/parser/src/schemas/cognito.ts | 25 ++++++++++ packages/parser/src/schemas/index.ts | 1 + .../parser/tests/unit/schema/cognito.test.ts | 49 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 packages/parser/src/schemas/cognito.ts create mode 100644 packages/parser/tests/unit/schema/cognito.test.ts diff --git a/packages/parser/src/schemas/cognito.ts b/packages/parser/src/schemas/cognito.ts new file mode 100644 index 0000000000..b4075d6439 --- /dev/null +++ b/packages/parser/src/schemas/cognito.ts @@ -0,0 +1,25 @@ +import { z } from 'zod'; + +export const PreSignupTriggerSchema = z.object({ + version: z.string(), + region: z.string(), + userPoolId: z.string(), + userName: z.string(), + callerContext: z.object({ + awsSdkVersion: z.string(), + clientId: z.string(), + }), + triggerSource: z.string(), + request: z.object({ + userAttributes: z.record(z.string(), z.string()), + validationData: z.record(z.string(), z.string()).nullable().optional(), + userNotFound: z.boolean().optional(), + }), + response: z + .object({ + autoConfirmUser: z.boolean().optional(), + autoVerifyEmail: z.boolean().optional(), + autoVerifyPhone: z.boolean().optional(), + }) + .optional(), +}); diff --git a/packages/parser/src/schemas/index.ts b/packages/parser/src/schemas/index.ts index ed70209a5a..5bfd757d67 100644 --- a/packages/parser/src/schemas/index.ts +++ b/packages/parser/src/schemas/index.ts @@ -47,6 +47,7 @@ export { KinesisFirehoseSqsRecordSchema, } from './kinesis-firehose.js'; export { LambdaFunctionUrlSchema } from './lambda.js'; +export { PreSignupTriggerSchema } from './cognito.js'; export { S3SqsEventNotificationSchema, S3EventNotificationEventBridgeSchema, diff --git a/packages/parser/tests/unit/schema/cognito.test.ts b/packages/parser/tests/unit/schema/cognito.test.ts new file mode 100644 index 0000000000..57f7ccaba6 --- /dev/null +++ b/packages/parser/tests/unit/schema/cognito.test.ts @@ -0,0 +1,49 @@ +import { describe, expect, it } from 'vitest'; +import { PreSignupTriggerSchema } from '../../../src/schemas/cognito.js'; +import { omit } from '../helpers/utils.js'; + +describe('Schema: Cognito PreSignupTrigger', () => { + // Prepare: Define a valid Cognito pre-signup event inline + const baseEvent = { + version: '1', + region: 'us-east-1', + userPoolId: 'us-east-1_ABC123', + userName: 'johndoe', + callerContext: { + awsSdkVersion: '2.814.0', + clientId: 'client123', + }, + triggerSource: 'PreSignUp_SignUp', + request: { + userAttributes: { + email: 'johndoe@example.com', + name: 'John Doe', + }, + validationData: null, + }, + response: { + autoConfirmUser: true, + autoVerifyEmail: false, + autoVerifyPhone: false, + }, + }; + + it('parses a valid pre-signup event', () => { + // Prepare + const event = structuredClone(baseEvent); + // Act + const result = PreSignupTriggerSchema.safeParse(event); + // Assess + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.userName).toEqual('johndoe'); + } + }); + + it('throws if the event is missing a required field', () => { + // Prepare + const event = omit(['userName'], structuredClone(baseEvent)); + // Act & Assess + expect(() => PreSignupTriggerSchema.parse(event)).toThrow(); + }); +}); From 118916307f21320250445561e9a8d58d5e7b4ae3 Mon Sep 17 00:00:00 2001 From: Vatsal Goel <144617902+VatsalGoel3@users.noreply.github.com> Date: Sun, 16 Mar 2025 22:18:54 -0600 Subject: [PATCH 2/7] feat(update): updated comments for clarity --- packages/parser/tests/unit/schema/cognito.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/parser/tests/unit/schema/cognito.test.ts b/packages/parser/tests/unit/schema/cognito.test.ts index 57f7ccaba6..e5c6131c28 100644 --- a/packages/parser/tests/unit/schema/cognito.test.ts +++ b/packages/parser/tests/unit/schema/cognito.test.ts @@ -3,7 +3,7 @@ import { PreSignupTriggerSchema } from '../../../src/schemas/cognito.js'; import { omit } from '../helpers/utils.js'; describe('Schema: Cognito PreSignupTrigger', () => { - // Prepare: Define a valid Cognito pre-signup event inline + // Prepare const baseEvent = { version: '1', region: 'us-east-1', From b7307e5707cd6929feb16398eb2dbf1a4dbcc001 Mon Sep 17 00:00:00 2001 From: Vatsal Goel <144617902+VatsalGoel3@users.noreply.github.com> Date: Tue, 18 Mar 2025 02:38:23 -0600 Subject: [PATCH 3/7] feat(parser): add comprehensive Cognito trigger schemas - Implemented 12 new Zod schemas for Cognito Lambda triggers: - Pre-Signup, Post-Confirmation, Pre-Authentication, Post-Authentication - Pre-Token Generation (V1 & V2), Migrate User - Custom Message, Custom Email Sender, Custom SMS Sender - Define Auth Challenge, Create Auth Challenge, Verify Auth Challenge - Updated schemas/index.ts for dedicated exports - Added unit tests in cognito.test.ts following Prepare/Act/Assess format --- packages/parser/src/schemas/cognito.ts | 493 ++++++++++++++++ packages/parser/src/schemas/index.ts | 16 +- .../parser/tests/unit/schema/cognito.test.ts | 556 +++++++++++++++++- 3 files changed, 1059 insertions(+), 6 deletions(-) diff --git a/packages/parser/src/schemas/cognito.ts b/packages/parser/src/schemas/cognito.ts index b4075d6439..5dd7b2cfd2 100644 --- a/packages/parser/src/schemas/cognito.ts +++ b/packages/parser/src/schemas/cognito.ts @@ -1,5 +1,39 @@ import { z } from 'zod'; +/** + * A zod schema for a Cognito Pre-Signup trigger event. + * + * @example + * ```json + * { + * "version": "1", + * "region": "us-east-1", + * "userPoolId": "us-east-1_ABC123", + * "userName": "johndoe", + * "callerContext": { + * "awsSdkVersion": "2.814.0", + * "clientId": "client123" + * }, + * "triggerSource": "PreSignUp_SignUp", + * "request": { + * "userAttributes": { + * "email": "johndoe@example.com", + * "name": "John Doe" + * }, + * "validationData": null, + * "clientMetadata": { + * "someKey": "someValue" + * } + * }, + * "response": { + * "autoConfirmUser": true, + * "autoVerifyEmail": false, + * "autoVerifyPhone": false + * } + * } + * ``` + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-sign-up.html} + */ export const PreSignupTriggerSchema = z.object({ version: z.string(), region: z.string(), @@ -13,6 +47,7 @@ export const PreSignupTriggerSchema = z.object({ request: z.object({ userAttributes: z.record(z.string(), z.string()), validationData: z.record(z.string(), z.string()).nullable().optional(), + clientMetadata: z.record(z.string(), z.string()).optional(), userNotFound: z.boolean().optional(), }), response: z @@ -23,3 +58,461 @@ export const PreSignupTriggerSchema = z.object({ }) .optional(), }); + +/** + * A zod schema for a Cognito Post-Confirmation trigger event. + * + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-post-confirmation.html} + * + * @example + * ```json + * { + * "request": { + * "userAttributes": { + * "email": "user@example.com", + * "name": "John Doe" + * }, + * "clientMetadata": { + * "customKey": "customValue" + * } + * }, + * "response": {} + * } + * ``` + */ +export const PostConfirmationTriggerSchema = z.object({ + request: z.object({ + userAttributes: z.record(z.string(), z.string()), + clientMetadata: z.record(z.string(), z.string().optional()), + }), + response: z.object({}), +}); + +/** + * A zod schema for a Cognito Pre-Authentication trigger event. + * + * @example + * ```json + * { + * "request": { + * "userAttributes": { + * "email": "user@example.com", + * "name": "John Doe" + * }, + * "validationData": { + * "someKey": "someValue" + * }, + * "userNotFound": false + * }, + * "response": {} + * } + * ``` + * * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-authentication.html} + */ +export const PreAuthenticationTriggerSchema = z.object({ + request: z.object({ + userAttributes: z.record(z.string(), z.string()), + validationData: z.record(z.string(), z.string()), + userNotFound: z.boolean(), + }), + response: z.object({}), +}); + +/** + * A zod schema for a Cognito Post-Authentication trigger event. + * + * @example + * ```json + * { + * "request": { + * "userAttributes": { + * "email": "user@example.com", + * "name": "John Doe" + * }, + * "newDeviceUsed": true, + * "clientMetadata": { + * "customKey": "customValue" + * } + * }, + * "response": {} + * } + * ``` + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-post-authentication.html} + */ +export const PostAuthenticationTriggerSchema = z.object({ + request: z.object({ + userAttributes: z.record(z.string(), z.string()), + newDeviceUsed: z.boolean(), + clientMetadata: z.record(z.string(), z.string()).optional(), + }), + response: z.object({}), +}); + +/** + * A zod schema for a Cognito Pre-Token Generation trigger event (version 1). + * + * @example + * ```json + * { + * "request": { + * "userAttributes": { "string": "string" }, + * "groupConfiguration": { + * "groupsToOverride": [ "string", "string" ], + * "iamRolesToOverride": [ "string", "string" ], + * "preferredRole": "string" + * }, + * "clientMetadata": { "string": "string" } + * }, + * "response": { + * "claimsOverrideDetails": { + * "claimsToAddOrOverride": { "string": "string" }, + * "claimsToSuppress": [ "string", "string" ], + * "groupOverrideDetails": { + * "groupsToOverride": [ "string", "string" ], + * "iamRolesToOverride": [ "string", "string" ], + * "preferredRole": "string" + * } + * } + * } + * } + * ``` + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html} + */ +export const PreTokenGenerationTriggerSchemaV1 = z.object({ + request: z.object({ + userAttributes: z.record(z.string(), z.string()), + groupConfiguration: z.object({ + groupsToOverride: z.array(z.string()), + iamRolesToOverride: z.array(z.string()), + preferredRole: z.string(), + }), + clientMetadata: z.record(z.string(), z.string()), + }), + response: z.object({ + claimsOverrideDetails: z.object({ + claimsToAddOrOverride: z.record(z.string(), z.string()), + claimsToSuppress: z.array(z.string()), + groupOverrideDetails: z.object({ + groupsToOverride: z.array(z.string()), + iamRolesToOverride: z.array(z.string()), + preferredRole: z.string(), + }), + }), + }), +}); + +/** + * A zod schema for a Cognito Pre-Token Generation trigger event (version 2). + * + * @example + * ```json + * { + * "request": { + * "userAttributes": { "string": "string" }, + * "scopes": [ "string", "string" ], + * "groupConfiguration": { + * "groupsToOverride": [ "string", "string" ], + * "iamRolesToOverride": [ "string", "string" ], + * "preferredRole": "string" + * }, + * "clientMetadata": { "string": "string" } + * }, + * "response": { + * "claimsAndScopeOverrideDetails": { + * "idTokenGeneration": { + * "claimsToAddOrOverride": { "string": "string" }, + * "claimsToSuppress": [ "string", "string" ] + * }, + * "accessTokenGeneration": { + * "claimsToAddOrOverride": { "string": "string" }, + * "claimsToSuppress": [ "string", "string" ], + * "scopesToAdd": [ "string", "string" ], + * "scopesToSuppress": [ "string", "string" ] + * }, + * "groupOverrideDetails": { + * "groupsToOverride": [ "string", "string" ], + * "iamRolesToOverride": [ "string", "string" ], + * "preferredRole": "string" + * } + * } + * } + * } + * ``` + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html} + */ +export const PreTokenGenerationTriggerSchemaV2 = z.object({ + request: z.object({ + userAttributes: z.record(z.string(), z.string()), + scopes: z.array(z.string()), + groupConfiguration: z.object({ + groupsToOverride: z.array(z.string()), + iamRolesToOverride: z.array(z.string()), + preferredRole: z.string(), + }), + clientMetadata: z.record(z.string(), z.string()), + }), + response: z.object({ + claimsAndScopeOverrideDetails: z.object({ + idTokenGeneration: z.object({ + claimsToAddOrOverride: z.record(z.any()), + claimsToSuppress: z.array(z.string()), + }), + accessTokenGeneration: z.object({ + claimsToAddOrOverride: z.record(z.any()), + claimsToSuppress: z.array(z.string()), + scopesToAdd: z.array(z.string()), + scopesToSuppress: z.array(z.string()), + }), + groupOverrideDetails: z.object({ + groupsToOverride: z.array(z.string()), + iamRolesToOverride: z.array(z.string()), + preferredRole: z.string(), + }), + }), + }), +}); + +/** + * A zod schema for a Cognito Migrate User trigger event. + * + * @example + * ```json + * { + * "userName": "string", + * "request": { + * "password": "string", + * "validationData": { "key": "value" }, + * "clientMetadata": { "key": "value" } + * }, + * "response": { + * "userAttributes": { "key": "value" }, + * "finalUserStatus": "string", + * "messageAction": "string", + * "desiredDeliveryMediums": [ "string" ], + * "forceAliasCreation": true, + * "enableSMSMFA": true + * } + * } + * ``` + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-migrate-user.html} + */ +export const MigrateUserTriggerSchema = z.object({ + userName: z.string(), + request: z.object({ + password: z.string(), + validationData: z.record(z.string(), z.string()), + clientMetadata: z.record(z.string(), z.string()), + }), + response: z.object({ + userAttributes: z.record(z.string(), z.string()), + finalUserStatus: z.string(), + messageAction: z.string(), + desiredDeliveryMediums: z.array(z.string()), + forceAliasCreation: z.boolean(), + enableSMSMFA: z.boolean(), + }), +}); + +/** + * A zod schema for a Cognito Custom Message trigger event. + * + * @example + * ```json + * { + * "request": { + * "userAttributes": { "string": "string", ... }, + * "codeParameter": "####", + * "usernameParameter": "string", + * "clientMetadata": { "string": "string", ... } + * }, + * "response": { + * "smsMessage": "string", + * "emailMessage": "string", + * "emailSubject": "string" + * } + * } + * ``` + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-message.html} + */ +export const CustomMessageTriggerSchema = z.object({ + request: z.object({ + userAttributes: z.record(z.string(), z.string()), + codeParameter: z.string(), + usernameParameter: z.string(), + clientMetadata: z.record(z.string(), z.string()).optional(), + }), + response: z.object({ + smsMessage: z.string(), + emailMessage: z.string(), + emailSubject: z.string(), + }), +}); + +/** + * A zod schema for a Cognito Custom Email Sender trigger event. + * + * @example + * ```json + * { + * "request": { + * "type": "customEmailSenderRequestV1", + * "code": "string", + * "clientMetadata": { "string": "string", ... }, + * "userAttributes": { "string": "string", ... } + * }, + * "response": {} + * } + * ``` + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-email-sender.html} + */ +export const CustomEmailSenderTriggerSchema = z.object({ + request: z.object({ + type: z.literal('customEmailSenderRequestV1'), + code: z.string(), + clientMetadata: z.record(z.string(), z.string()).optional(), + userAttributes: z.record(z.string(), z.string()), + }), + response: z.object({}), +}); + +/** + * A zod schema for a Cognito Custom SMS Sender trigger event. + * + * @example + * ```json + * { + * "request": { + * "type": "customSMSSenderRequestV1", + * "code": "string", + * "clientMetadata": { "string": "string", ... }, + * "userAttributes": { "string": "string", ... } + * }, + * "response": {} + * } + * ``` + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-sms-sender.html} + */ +export const CustomSMSSenderTriggerSchema = z.object({ + request: z.object({ + type: z.literal('customSMSSenderRequestV1'), + code: z.string(), + clientMetadata: z.record(z.string(), z.string()).optional(), + userAttributes: z.record(z.string(), z.string()), + }), + response: z.object({}), +}); + +const ChallengeResultSchema = z.object({}); + +/** + * A zod schema for a Cognito Define Auth Challenge trigger event. + * + * @example + * ```json + * { + * "request": { + * "userAttributes": { "email": "user@example.com", "name": "John Doe" }, + * "session": [ + * { + * "challengeName": "SRP_A", + * "challengeResult": true, + * "challengeMetadata": "metadata" + * } + * ], + * "clientMetadata": { "key": "value" }, + * "userNotFound": false + * }, + * "response": { + * "challengeName": "PASSWORD_VERIFIER", + * "issueTokens": false, + * "failAuthentication": false + * } + * } + * ``` + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-define-auth-challenge.html} + */ +export const DefineAuthChallengeSchema = z.object({ + request: z.object({ + userAttributes: z.record(z.string(), z.string()), + session: z.array(ChallengeResultSchema), + clientMetadata: z.record(z.string(), z.string()).optional(), + userNotFound: z.boolean(), + }), + response: z.object({ + challengeName: z.string(), + issueTokens: z.boolean(), + failAuthentication: z.boolean(), + }), +}); + +/** + * A zod schema for a Cognito Create Auth Challenge trigger event. + * + * @example + * ```json + * { + * "request": { + * "userAttributes": { "email": "user@example.com", "name": "John Doe" }, + * "challengeName": "CUSTOM_CHALLENGE", + * "session": [ + * { "challengeName": "SRP_A", "challengeResult": true, "challengeMetadata": "metadata" } + * ], + * "clientMetadata": { "key": "value" }, + * "userNotFound": false + * }, + * "response": { + * "publicChallengeParameters": { "captchaUrl": "url/123.jpg" }, + * "privateChallengeParameters": { "answer": "5" }, + * "challengeMetadata": "custom metadata" + * } + * } + * ``` + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-create-auth-challenge.html} + */ +export const CreateAuthChallengeSchema = z.object({ + request: z.object({ + userAttributes: z.record(z.string(), z.string()), + challengeName: z.string(), + session: z.array(ChallengeResultSchema), + clientMetadata: z.record(z.string(), z.string()).optional(), + userNotFound: z.boolean(), + }), + response: z.object({ + publicChallengeParameters: z.record(z.string()), + privateChallengeParameters: z.record(z.string()), + challengeMetadata: z.string(), + }), +}); + +/** + * A zod schema for a Cognito Verify Auth Challenge Response trigger event. + * + * @example + * ```json + * { + * "request": { + * "userAttributes": { "email": "user@example.com", "name": "John Doe" }, + * "privateChallengeParameters": { "answer": "expectedAnswer" }, + * "challengeAnswer": "userAnswer", + * "clientMetadata": { "key": "value" }, + * "userNotFound": false + * }, + * "response": { + * "answerCorrect": true + * } + * } + * ``` + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-verify-auth-challenge-response.html} + */ +export const VerifyAuthChallengeSchema = z.object({ + request: z.object({ + userAttributes: z.record(z.string(), z.string()), + privateChallengeParameters: z.record(z.string(), z.string()), + challengeAnswer: z.string(), + clientMetadata: z.record(z.string(), z.string()).optional(), + userNotFound: z.boolean(), + }), + response: z.object({ + answerCorrect: z.boolean(), + }), +}); diff --git a/packages/parser/src/schemas/index.ts b/packages/parser/src/schemas/index.ts index 5bfd757d67..3ba962bca1 100644 --- a/packages/parser/src/schemas/index.ts +++ b/packages/parser/src/schemas/index.ts @@ -25,6 +25,21 @@ export { CloudWatchLogsDecodeSchema, CloudWatchLogsSchema, } from './cloudwatch.js'; +export { + PreSignupTriggerSchema, + PostConfirmationTriggerSchema, + PreAuthenticationTriggerSchema, + PostAuthenticationTriggerSchema, + PreTokenGenerationTriggerSchemaV1, + PreTokenGenerationTriggerSchemaV2, + MigrateUserTriggerSchema, + CustomMessageTriggerSchema, + CustomEmailSenderTriggerSchema, + CustomSMSSenderTriggerSchema, + DefineAuthChallengeSchema, + CreateAuthChallengeSchema, + VerifyAuthChallengeSchema, +} from './cognito.js'; export { DynamoDBStreamSchema, DynamoDBStreamToKinesisRecord, @@ -47,7 +62,6 @@ export { KinesisFirehoseSqsRecordSchema, } from './kinesis-firehose.js'; export { LambdaFunctionUrlSchema } from './lambda.js'; -export { PreSignupTriggerSchema } from './cognito.js'; export { S3SqsEventNotificationSchema, S3EventNotificationEventBridgeSchema, diff --git a/packages/parser/tests/unit/schema/cognito.test.ts b/packages/parser/tests/unit/schema/cognito.test.ts index e5c6131c28..0fb2c710f2 100644 --- a/packages/parser/tests/unit/schema/cognito.test.ts +++ b/packages/parser/tests/unit/schema/cognito.test.ts @@ -1,10 +1,24 @@ import { describe, expect, it } from 'vitest'; -import { PreSignupTriggerSchema } from '../../../src/schemas/cognito.js'; +import { + CreateAuthChallengeSchema, + CustomEmailSenderTriggerSchema, + CustomMessageTriggerSchema, + CustomSMSSenderTriggerSchema, + DefineAuthChallengeSchema, + MigrateUserTriggerSchema, + PostAuthenticationTriggerSchema, + PostConfirmationTriggerSchema, + PreAuthenticationTriggerSchema, + PreSignupTriggerSchema, + PreTokenGenerationTriggerSchemaV1, + PreTokenGenerationTriggerSchemaV2, + VerifyAuthChallengeSchema, +} from '../../../src/schemas/cognito.js'; import { omit } from '../helpers/utils.js'; describe('Schema: Cognito PreSignupTrigger', () => { // Prepare - const baseEvent = { + const basePreSignupEvent = { version: '1', region: 'us-east-1', userPoolId: 'us-east-1_ABC123', @@ -20,6 +34,9 @@ describe('Schema: Cognito PreSignupTrigger', () => { name: 'John Doe', }, validationData: null, + clientMetadata: { + someKey: 'someValue', + }, }, response: { autoConfirmUser: true, @@ -30,7 +47,7 @@ describe('Schema: Cognito PreSignupTrigger', () => { it('parses a valid pre-signup event', () => { // Prepare - const event = structuredClone(baseEvent); + const event = structuredClone(basePreSignupEvent); // Act const result = PreSignupTriggerSchema.safeParse(event); // Assess @@ -40,10 +57,539 @@ describe('Schema: Cognito PreSignupTrigger', () => { } }); - it('throws if the event is missing a required field', () => { + it('throws if the pre-signup event is missing a required field', () => { // Prepare - const event = omit(['userName'], structuredClone(baseEvent)); + const event = omit(['userName'], structuredClone(basePreSignupEvent)); // Act & Assess expect(() => PreSignupTriggerSchema.parse(event)).toThrow(); }); }); + +describe('Schema: Cognito PostConfirmationTrigger', () => { + // Prepare + const basePostConfirmationEvent = { + request: { + userAttributes: { + email: 'user@example.com', + name: 'John Doe', + }, + clientMetadata: { + customKey: 'customValue', + }, + }, + response: {}, + }; + + it('parses a valid post-confirmation event', () => { + // Prepare + const event = structuredClone(basePostConfirmationEvent); + // Act + const result = PostConfirmationTriggerSchema.safeParse(event); + // Assess + expect(result.success).toBe(true); + }); + + it('throws if the post-confirmation event is missing a required field', () => { + // Prepare + const event = { + ...basePostConfirmationEvent, + request: omit( + ['userAttributes'], + structuredClone(basePostConfirmationEvent.request) + ), + }; + // Act & Assess + expect(() => PostConfirmationTriggerSchema.parse(event)).toThrow(); + }); +}); + +describe('Schema: Cognito PreAuthenticationTrigger', () => { + // Prepare + const basePreAuthEvent = { + request: { + userAttributes: { + email: 'user@example.com', + name: 'John Doe', + }, + validationData: { + someKey: 'someValue', + }, + userNotFound: false, + }, + response: {}, + }; + + it('parses a valid pre-authentication event', () => { + // Prepare + const event = structuredClone(basePreAuthEvent); + // Act + const result = PreAuthenticationTriggerSchema.safeParse(event); + // Assess + expect(result.success).toBe(true); + }); + + it('throws if the pre-authentication event is missing a required field', () => { + // Prepare + const event = { + ...basePreAuthEvent, + request: omit( + ['userAttributes'], + structuredClone(basePreAuthEvent.request) + ), + }; + // Act & Assess + expect(() => PreAuthenticationTriggerSchema.parse(event)).toThrow(); + }); +}); + +describe('Schema: Cognito PostAuthenticationTrigger', () => { + // Prepare + const basePostAuthEvent = { + request: { + userAttributes: { + email: 'user@example.com', + name: 'John Doe', + }, + newDeviceUsed: true, + clientMetadata: { + customKey: 'customValue', + }, + }, + response: {}, + }; + + it('parses a valid post-authentication event', () => { + // Prepare + const event = structuredClone(basePostAuthEvent); + // Act + const result = PostAuthenticationTriggerSchema.safeParse(event); + // Assess + expect(result.success).toBe(true); + }); + + it('throws if the post-authentication event is missing a required field', () => { + // Prepare + const event = { + ...basePostAuthEvent, + request: omit( + ['newDeviceUsed'], + structuredClone(basePostAuthEvent.request) + ), + }; + // Act & Assess + expect(() => PostAuthenticationTriggerSchema.parse(event)).toThrow(); + }); +}); + +describe('Schema: Cognito PreTokenGenerationTrigger V1', () => { + // Prepare + const basePreTokenGenEventV1 = { + request: { + userAttributes: { + email: 'user@example.com', + name: 'John Doe', + }, + groupConfiguration: { + groupsToOverride: ['group1', 'group2'], + iamRolesToOverride: ['role1', 'role2'], + preferredRole: 'role1', + }, + clientMetadata: { + key1: 'value1', + }, + }, + response: { + claimsOverrideDetails: { + claimsToAddOrOverride: { + customClaim: 'customValue', + }, + claimsToSuppress: ['email'], + groupOverrideDetails: { + groupsToOverride: ['newGroup1', 'newGroup2'], + iamRolesToOverride: ['newRole1', 'newRole2'], + preferredRole: 'newRole1', + }, + }, + }, + }; + + it('parses a valid pre-token generation V1 event', () => { + // Prepare + const event = structuredClone(basePreTokenGenEventV1); + // Act + const result = PreTokenGenerationTriggerSchemaV1.safeParse(event); + // Assess + expect(result.success).toBe(true); + }); + + it('throws if the pre-token generation V1 event is missing a required field', () => { + // Prepare + const event = { + ...basePreTokenGenEventV1, + request: omit( + ['groupConfiguration'], + structuredClone(basePreTokenGenEventV1.request) + ), + }; + // Act & Assess + expect(() => PreTokenGenerationTriggerSchemaV1.parse(event)).toThrow(); + }); +}); + +describe('Schema: Cognito PreTokenGenerationTrigger V2', () => { + // Prepare + const basePreTokenGenEventV2 = { + request: { + userAttributes: { + email: 'user@example.com', + name: 'John Doe', + }, + scopes: ['openid', 'email'], + groupConfiguration: { + groupsToOverride: ['group1', 'group2'], + iamRolesToOverride: ['role1', 'role2'], + preferredRole: 'role1', + }, + clientMetadata: { + key1: 'value1', + }, + }, + response: { + claimsAndScopeOverrideDetails: { + idTokenGeneration: { + claimsToAddOrOverride: { + customClaim: 'customValue', + }, + claimsToSuppress: ['email'], + }, + accessTokenGeneration: { + claimsToAddOrOverride: { + customClaim: 'customValue', + }, + claimsToSuppress: ['email'], + scopesToAdd: ['openid', 'profile'], + scopesToSuppress: ['phone'], + }, + groupOverrideDetails: { + groupsToOverride: ['newGroup1', 'newGroup2'], + iamRolesToOverride: ['newRole1', 'newRole2'], + preferredRole: 'newRole1', + }, + }, + }, + }; + + it('parses a valid pre-token generation V2 event', () => { + // Prepare + const event = structuredClone(basePreTokenGenEventV2); + // Act + const result = PreTokenGenerationTriggerSchemaV2.safeParse(event); + // Assess + expect(result.success).toBe(true); + }); + + it('throws if the pre-token generation V2 event is missing a required field', () => { + // Prepare + const event = { + ...basePreTokenGenEventV2, + request: omit( + ['scopes'], + structuredClone(basePreTokenGenEventV2.request) + ), + }; + // Act & Assess + expect(() => PreTokenGenerationTriggerSchemaV2.parse(event)).toThrow(); + }); +}); + +describe('Schema: Cognito MigrateUserTrigger', () => { + // Prepare + const baseMigrateUserEvent = { + userName: 'testuser', + request: { + password: 'TestPass123!', + validationData: { + key1: 'value1', + }, + clientMetadata: { + key2: 'value2', + }, + }, + response: { + userAttributes: { + email: 'testuser@example.com', + name: 'Test User', + }, + finalUserStatus: 'CONFIRMED', + messageAction: 'SUPPRESS', + desiredDeliveryMediums: ['EMAIL'], + forceAliasCreation: true, + enableSMSMFA: false, + }, + }; + + it('parses a valid migrate user event', () => { + // Prepare + const event = structuredClone(baseMigrateUserEvent); + // Act + const result = MigrateUserTriggerSchema.safeParse(event); + // Assess + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.userName).toEqual('testuser'); + } + }); + + it('throws if the migrate user event is missing a required field', () => { + // Prepare + const event = omit(['userName'], structuredClone(baseMigrateUserEvent)); + // Act & Assess + expect(() => MigrateUserTriggerSchema.parse(event)).toThrow(); + }); +}); + +describe('Schema: Cognito CustomMessageTrigger', () => { + // Prepare + const baseCustomMessageEvent = { + request: { + userAttributes: { email: 'test@example.com', name: 'Test User' }, + codeParameter: '####', + usernameParameter: 'TestUser', + clientMetadata: { key: 'value' }, + }, + response: { + smsMessage: 'Your code is ####', + emailMessage: 'Your verification code is ####', + emailSubject: 'Verification', + }, + }; + + it('parses a valid custom message event', () => { + // Prepare + const event = structuredClone(baseCustomMessageEvent); + // Act + const result = CustomMessageTriggerSchema.safeParse(event); + // Assess + expect(result.success).toBe(true); + }); + + it('throws if the custom message event is missing a required field', () => { + // Prepare + const event = { + ...baseCustomMessageEvent, + request: omit( + ['codeParameter'], + structuredClone(baseCustomMessageEvent.request) + ), + }; + // Act & Assess + expect(() => CustomMessageTriggerSchema.parse(event)).toThrow(); + }); +}); + +describe('Schema: Cognito CustomEmailSenderTrigger', () => { + // Prepare + const baseCustomEmailSenderEvent = { + request: { + type: 'customEmailSenderRequestV1', + code: 'encryptedCode', + clientMetadata: { key: 'value' }, + userAttributes: { email: 'test@example.com', name: 'Test User' }, + }, + response: {}, + }; + + it('parses a valid custom email sender event', () => { + // Prepare + const event = structuredClone(baseCustomEmailSenderEvent); + // Act + const result = CustomEmailSenderTriggerSchema.safeParse(event); + // Assess + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.response).toEqual({}); + } + }); + + it('throws if the custom email sender event is missing a required field', () => { + // Prepare + const event = { + ...baseCustomEmailSenderEvent, + request: omit( + ['code'], + structuredClone(baseCustomEmailSenderEvent.request) + ), + }; + // Act & Assess + expect(() => CustomEmailSenderTriggerSchema.parse(event)).toThrow(); + }); +}); + +describe('Schema: Cognito CustomSMSSenderTrigger', () => { + // Prepare + const baseCustomSMSSenderEvent = { + request: { + type: 'customSMSSenderRequestV1', + code: 'encryptedCode', + clientMetadata: { key: 'value' }, + userAttributes: { + phone_number: '+1234567890', + email: 'test@example.com', + }, + }, + response: {}, + }; + + it('parses a valid custom SMS sender event', () => { + // Prepare + const event = structuredClone(baseCustomSMSSenderEvent); + // Act + const result = CustomSMSSenderTriggerSchema.safeParse(event); + // Assess + expect(result.success).toBe(true); + }); + + it('throws if the custom SMS sender event is missing a required field', () => { + // Prepare + const event = { + ...baseCustomSMSSenderEvent, + request: omit( + ['code'], + structuredClone(baseCustomSMSSenderEvent.request) + ), + }; + // Act & Assess + expect(() => CustomSMSSenderTriggerSchema.parse(event)).toThrow(); + }); +}); + +describe('Schema: Cognito DefineAuthChallenge', () => { + const baseDefineAuthChallengeEvent = { + request: { + userAttributes: { email: 'user@example.com', name: 'John Doe' }, + session: [ + { + challengeName: 'SRP_A', + challengeResult: true, + challengeMetadata: 'metadata', + }, + ], + clientMetadata: { key: 'value' }, + userNotFound: false, + }, + response: { + challengeName: 'PASSWORD_VERIFIER', + issueTokens: false, + failAuthentication: false, + }, + }; + + it('parses a valid define auth challenge event', () => { + const event = structuredClone(baseDefineAuthChallengeEvent); + const result = DefineAuthChallengeSchema.safeParse(event); + expect(result.success).toBe(true); + }); + + it('throws if the define auth challenge event is missing a required field', () => { + const event = { + ...baseDefineAuthChallengeEvent, + request: omit( + ['userAttributes'], + structuredClone(baseDefineAuthChallengeEvent.request) + ), + }; + expect(() => DefineAuthChallengeSchema.parse(event)).toThrow(); + }); +}); + +describe('Schema: Cognito CreateAuthChallenge', () => { + // Prepare + const baseCreateAuthChallengeEvent = { + request: { + userAttributes: { email: 'user@example.com', name: 'John Doe' }, + challengeName: 'CUSTOM_CHALLENGE', + session: [ + { + challengeName: 'SRP_A', + challengeResult: true, + challengeMetadata: 'metadata', + }, + ], + clientMetadata: { key: 'value' }, + userNotFound: false, + }, + response: { + publicChallengeParameters: { captchaUrl: 'url/123.jpg' }, + privateChallengeParameters: { answer: '5' }, + challengeMetadata: 'custom metadata', + }, + }; + + it('parses a valid create auth challenge event', () => { + // Prepare + const event = structuredClone(baseCreateAuthChallengeEvent); + // Act + const result = CreateAuthChallengeSchema.safeParse(event); + // Assess + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.response.publicChallengeParameters.captchaUrl).toEqual( + 'url/123.jpg' + ); + } + }); + + it('throws if the create auth challenge event is missing a required field', () => { + // Prepare + const event = { + ...baseCreateAuthChallengeEvent, + request: omit( + ['challengeName'], + structuredClone(baseCreateAuthChallengeEvent.request) + ), + }; + // Act & Assess + expect(() => CreateAuthChallengeSchema.parse(event)).toThrow(); + }); +}); + +describe('Schema: Cognito VerifyAuthChallenge', () => { + // Prepare + const baseVerifyAuthChallengeEvent = { + request: { + userAttributes: { email: 'user@example.com', name: 'John Doe' }, + privateChallengeParameters: { answer: 'expectedAnswer' }, + challengeAnswer: 'expectedAnswer', + clientMetadata: { key: 'value' }, + userNotFound: false, + }, + response: { + answerCorrect: true, + }, + }; + + it('parses a valid verify auth challenge event', () => { + // Prepare + const event = structuredClone(baseVerifyAuthChallengeEvent); + // Act + const result = VerifyAuthChallengeSchema.safeParse(event); + // Assess + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.response.answerCorrect).toBe(true); + } + }); + + it('throws if the verify auth challenge event is missing a required field', () => { + // Prepare + const event = { + ...baseVerifyAuthChallengeEvent, + request: omit( + ['privateChallengeParameters'], + structuredClone(baseVerifyAuthChallengeEvent.request) + ), + }; + // Act & Assess + expect(() => VerifyAuthChallengeSchema.parse(event)).toThrow(); + }); +}); From 1da3abb53fb65267e44b0a3e077d5c35a838e1e3 Mon Sep 17 00:00:00 2001 From: Vatsal Goel <144617902+VatsalGoel3@users.noreply.github.com> Date: Tue, 18 Mar 2025 02:46:06 -0600 Subject: [PATCH 4/7] test(parser): replace hard-coded password in MigrateUserTrigger test with a generic placeholder --- packages/parser/tests/unit/schema/cognito.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/parser/tests/unit/schema/cognito.test.ts b/packages/parser/tests/unit/schema/cognito.test.ts index 0fb2c710f2..fc75576775 100644 --- a/packages/parser/tests/unit/schema/cognito.test.ts +++ b/packages/parser/tests/unit/schema/cognito.test.ts @@ -307,7 +307,7 @@ describe('Schema: Cognito MigrateUserTrigger', () => { const baseMigrateUserEvent = { userName: 'testuser', request: { - password: 'TestPass123!', + password: 'dummyPass123!', validationData: { key1: 'value1', }, From 50094ef78b7dfa8ccf95cd72243ee54d57062c69 Mon Sep 17 00:00:00 2001 From: Vatsal Goel <144617902+VatsalGoel3@users.noreply.github.com> Date: Tue, 18 Mar 2025 08:32:10 -0600 Subject: [PATCH 5/7] feat(parser): add ChallengeResult schema for authentication flows --- packages/parser/src/schemas/cognito.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/parser/src/schemas/cognito.ts b/packages/parser/src/schemas/cognito.ts index 5dd7b2cfd2..452f8b52ff 100644 --- a/packages/parser/src/schemas/cognito.ts +++ b/packages/parser/src/schemas/cognito.ts @@ -402,7 +402,11 @@ export const CustomSMSSenderTriggerSchema = z.object({ response: z.object({}), }); -const ChallengeResultSchema = z.object({}); +const ChallengeResultSchema = z.object({ + challengeName: z.string(), + challengeResult: z.boolean(), + challengeMetadata: z.string().optional(), +}); /** * A zod schema for a Cognito Define Auth Challenge trigger event. From b26b1531e4820acecd7334b1cd68e8f001ce881a Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 1 Apr 2025 17:45:14 +0200 Subject: [PATCH 6/7] chore: fix and rework PR --- docs/utilities/parser.md | 12 + packages/parser/src/schemas/cognito.ts | 467 ++++++++----- .../parser/tests/events/cognito/base.json | 13 + .../parser/tests/unit/schema/cognito.test.ts | 659 +++++++----------- 4 files changed, 552 insertions(+), 599 deletions(-) create mode 100644 packages/parser/tests/events/cognito/base.json diff --git a/docs/utilities/parser.md b/docs/utilities/parser.md index da315c47c8..20c400bc70 100644 --- a/docs/utilities/parser.md +++ b/docs/utilities/parser.md @@ -69,6 +69,18 @@ Parser comes with the following built-in schemas: | **CloudFormationCustomResourceUpdateSchema** | Lambda Event Source payload for AWS CloudFormation `UPDATE` operation | | **CloudFormationCustomResourceDeleteSchema** | Lambda Event Source payload for AWS CloudFormation `DELETE` operation | | **CloudwatchLogsSchema** | Lambda Event Source payload for Amazon CloudWatch Logs | +| **PreSignupTriggerSchema** | Lambda Event Source payload for Amazon Cognito Pre Sign-up trigger | +| **PostConfirmationTriggerSchema** | Lambda Event Source payload for Amazon Cognito Post Confirmation trigger | +| **PreTokenGenerationTriggerSchema** | Lambda Event Source payload for Amazon Cognito Pre Token Generation trigger | +| **CustomMessageTriggerSchema** | Lambda Event Source payload for Amazon Cognito Custom Message trigger | +| **MigrateUserTriggerSchema** | Lambda Event Source payload for Amazon Cognito User Migration trigger | +| **CustomSMSTriggerSchema** | Lambda Event Source payload for Amazon Cognito Custom SMS trigger | +| **CustomEmailTriggerSchema** | Lambda Event Source payload for Amazon Cognito Custom Email trigger | +| **DefineAuthChallengeTriggerSchema** | Lambda Event Source payload for Amazon Cognito Define Auth Challenge trigger | +| **CreateAuthChallengeTriggerSchema** | Lambda Event Source payload for Amazon Cognito Create Auth Challenge trigger | +| **VerifyAuthChallengeResponseTriggerSchema** | Lambda Event Source payload for Amazon Cognito Verify Auth Challenge Response trigger | +| **PreTokenGenerationTriggerSchemaV1** | Lambda Event Source payload for Amazon Cognito Pre Token Generation trigger v1 | +| **PreTokenGenerationTriggerSchemaV2AndV3** | Lambda Event Source payload for Amazon Cognito Pre Token Generation trigger v2 and v3 | | **DynamoDBStreamSchema** | Lambda Event Source payload for Amazon DynamoDB Streams | | **EventBridgeSchema** | Lambda Event Source payload for Amazon EventBridge | | **KafkaMskEventSchema** | Lambda Event Source payload for AWS MSK payload | diff --git a/packages/parser/src/schemas/cognito.ts b/packages/parser/src/schemas/cognito.ts index 452f8b52ff..d38b582722 100644 --- a/packages/parser/src/schemas/cognito.ts +++ b/packages/parser/src/schemas/cognito.ts @@ -1,5 +1,24 @@ import { z } from 'zod'; +/** + * Base schema including the common parameters for all Cognito trigger events. + * + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-working-with-lambda-triggers.html#cognito-user-pools-lambda-trigger-syntax-shared | Amazon Cognito Developer Guide} + */ +const CognitoTriggerBaseSchema = z.object({ + version: z.string(), + triggerSource: z.string(), + region: z.string(), + userPoolId: z.string(), + userName: z.string().optional(), + callerContext: z.object({ + awsSdkVersion: z.string(), + clientId: z.string(), + }), + request: z.object({}), + response: z.object({}), +}); + /** * A zod schema for a Cognito Pre-Signup trigger event. * @@ -7,6 +26,7 @@ import { z } from 'zod'; * ```json * { * "version": "1", + * "triggerSource": "PreSignUp_SignUp", * "region": "us-east-1", * "userPoolId": "us-east-1_ABC123", * "userName": "johndoe", @@ -14,7 +34,6 @@ import { z } from 'zod'; * "awsSdkVersion": "2.814.0", * "clientId": "client123" * }, - * "triggerSource": "PreSignUp_SignUp", * "request": { * "userAttributes": { * "email": "johndoe@example.com", @@ -26,47 +45,45 @@ import { z } from 'zod'; * } * }, * "response": { - * "autoConfirmUser": true, + * "autoConfirmUser": false, * "autoVerifyEmail": false, * "autoVerifyPhone": false * } * } * ``` - * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-sign-up.html} + * + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-sign-up.html | Amazon Cognito Developer Guide} */ -export const PreSignupTriggerSchema = z.object({ - version: z.string(), - region: z.string(), - userPoolId: z.string(), - userName: z.string(), - callerContext: z.object({ - awsSdkVersion: z.string(), - clientId: z.string(), - }), - triggerSource: z.string(), +const PreSignupTriggerSchema = CognitoTriggerBaseSchema.extend({ + triggerSource: z.literal('PreSignUp_SignUp'), request: z.object({ userAttributes: z.record(z.string(), z.string()), - validationData: z.record(z.string(), z.string()).nullable().optional(), + validationData: z.record(z.string(), z.string()).nullable(), clientMetadata: z.record(z.string(), z.string()).optional(), userNotFound: z.boolean().optional(), }), - response: z - .object({ - autoConfirmUser: z.boolean().optional(), - autoVerifyEmail: z.boolean().optional(), - autoVerifyPhone: z.boolean().optional(), - }) - .optional(), + response: z.object({ + autoConfirmUser: z.literal(false), + autoVerifyEmail: z.literal(false), + autoVerifyPhone: z.literal(false), + }), }); /** * A zod schema for a Cognito Post-Confirmation trigger event. * - * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-post-confirmation.html} - * * @example * ```json * { + * "version": "1", + * "triggerSource": "PostConfirmation_ConfirmSignUp", + * "region": "us-east-1", + * "userPoolId": "us-east-1_ABC123", + * "userName": "johndoe", + * "callerContext": { + * "awsSdkVersion": "2.814.0", + * "clientId": "client123" + * }, * "request": { * "userAttributes": { * "email": "user@example.com", @@ -79,11 +96,14 @@ export const PreSignupTriggerSchema = z.object({ * "response": {} * } * ``` + * + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-post-confirmation.html | Amazon Cognito Developer Guide} */ -export const PostConfirmationTriggerSchema = z.object({ +const PostConfirmationTriggerSchema = CognitoTriggerBaseSchema.extend({ + triggerSource: z.literal('PostConfirmation_ConfirmSignUp'), request: z.object({ userAttributes: z.record(z.string(), z.string()), - clientMetadata: z.record(z.string(), z.string().optional()), + clientMetadata: z.record(z.string(), z.string()).optional(), }), response: z.object({}), }); @@ -94,6 +114,10 @@ export const PostConfirmationTriggerSchema = z.object({ * @example * ```json * { + * "version": "1", + * "triggerSource": "PreAuthentication_Authentication", + * "region": "us-east-1", + * "userPoolId": "us-east-1_ABC123", * "request": { * "userAttributes": { * "email": "user@example.com", @@ -107,13 +131,15 @@ export const PostConfirmationTriggerSchema = z.object({ * "response": {} * } * ``` - * * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-authentication.html} + * + * * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-authentication.html | Amazon Cognito Developer Guide} */ -export const PreAuthenticationTriggerSchema = z.object({ +const PreAuthenticationTriggerSchema = CognitoTriggerBaseSchema.extend({ + triggerSource: z.literal('PreAuthentication_Authentication'), request: z.object({ userAttributes: z.record(z.string(), z.string()), - validationData: z.record(z.string(), z.string()), - userNotFound: z.boolean(), + validationData: z.record(z.string(), z.string()).optional(), + userNotFound: z.boolean().optional(), }), response: z.object({}), }); @@ -124,6 +150,15 @@ export const PreAuthenticationTriggerSchema = z.object({ * @example * ```json * { + * "version": "1", + * "triggerSource": "PostAuthentication_Authentication", + * "region": "us-east-1", + * "userPoolId": "us-east-1_ABC123", + * "userName": "johndoe", + * "callerContext": { + * "awsSdkVersion": "2.814.0", + * "clientId": "client123" + * }, * "request": { * "userAttributes": { * "email": "user@example.com", @@ -137,15 +172,38 @@ export const PreAuthenticationTriggerSchema = z.object({ * "response": {} * } * ``` - * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-post-authentication.html} + * + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-post-authentication.html | Amazon Cognito Developer Guide} */ -export const PostAuthenticationTriggerSchema = z.object({ +const PostAuthenticationTriggerSchema = CognitoTriggerBaseSchema.extend({ + triggerSource: z.literal('PostAuthentication_Authentication'), request: z.object({ userAttributes: z.record(z.string(), z.string()), - newDeviceUsed: z.boolean(), + newDeviceUsed: z.boolean().optional(), clientMetadata: z.record(z.string(), z.string()).optional(), }), - response: z.object({}), +}); + +/** + * A zod schema for a Cognito Pre-Token Generation trigger event group configuration. + * + * Use this schema to extend the {@link PreTokenGenerationTriggerRequestSchema} for the `groupConfiguration` property. + */ +const PreTokenGenerationTriggerGroupConfigurationSchema = z.object({ + groupsToOverride: z.array(z.string()), + iamRolesToOverride: z.array(z.string()), + preferredRole: z.string().optional(), +}); + +/** + * A zod schema for a Cognito Pre-Token Generation trigger event request. + * + * Use this schema to extend the {@link PreTokenGenerationTriggerSchemaV1} and {@link PreTokenGenerationTriggerSchemaV2AndV3} for the `request` property. + */ +const PreTokenGenerationTriggerRequestSchema = z.object({ + userAttributes: z.record(z.string(), z.string()), + groupConfiguration: PreTokenGenerationTriggerGroupConfigurationSchema, + clientMetadata: z.record(z.string(), z.string()).optional(), }); /** @@ -154,6 +212,15 @@ export const PostAuthenticationTriggerSchema = z.object({ * @example * ```json * { + * "version": "1", + * "triggerSource": "TokenGeneration_Authentication", + * "region": "us-east-1", + * "userPoolId": "us-east-1_ABC123", + * "userName": "johndoe", + * "callerContext": { + * "awsSdkVersion": "2.814.0", + * "clientId": "client123" + * }, * "request": { * "userAttributes": { "string": "string" }, * "groupConfiguration": { @@ -163,112 +230,50 @@ export const PostAuthenticationTriggerSchema = z.object({ * }, * "clientMetadata": { "string": "string" } * }, - * "response": { - * "claimsOverrideDetails": { - * "claimsToAddOrOverride": { "string": "string" }, - * "claimsToSuppress": [ "string", "string" ], - * "groupOverrideDetails": { - * "groupsToOverride": [ "string", "string" ], - * "iamRolesToOverride": [ "string", "string" ], - * "preferredRole": "string" - * } - * } - * } + * "response": {} * } * ``` - * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html} + * + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html | Amazon Cognito Developer Guide} */ -export const PreTokenGenerationTriggerSchemaV1 = z.object({ - request: z.object({ - userAttributes: z.record(z.string(), z.string()), - groupConfiguration: z.object({ - groupsToOverride: z.array(z.string()), - iamRolesToOverride: z.array(z.string()), - preferredRole: z.string(), - }), - clientMetadata: z.record(z.string(), z.string()), - }), - response: z.object({ - claimsOverrideDetails: z.object({ - claimsToAddOrOverride: z.record(z.string(), z.string()), - claimsToSuppress: z.array(z.string()), - groupOverrideDetails: z.object({ - groupsToOverride: z.array(z.string()), - iamRolesToOverride: z.array(z.string()), - preferredRole: z.string(), - }), - }), - }), +const PreTokenGenerationTriggerSchemaV1 = CognitoTriggerBaseSchema.extend({ + request: PreTokenGenerationTriggerRequestSchema, }); /** - * A zod schema for a Cognito Pre-Token Generation trigger event (version 2). + * A zod schema for a Cognito Pre-Token Generation trigger event (version 2 and 3). * * @example * ```json * { + * "version": "2", + * "triggerSource": "TokenGeneration_Authentication", + * "region": "us-east-1", + * "userPoolId": "us-east-1_ABC123", + * "userName": "johndoe", + * "callerContext": { + * "awsSdkVersion": "2.814.0", + * "clientId": "client123" + * }, * "request": { * "userAttributes": { "string": "string" }, - * "scopes": [ "string", "string" ], * "groupConfiguration": { * "groupsToOverride": [ "string", "string" ], * "iamRolesToOverride": [ "string", "string" ], * "preferredRole": "string" * }, + * "scopes": [ "string", "string" ], * "clientMetadata": { "string": "string" } - * }, - * "response": { - * "claimsAndScopeOverrideDetails": { - * "idTokenGeneration": { - * "claimsToAddOrOverride": { "string": "string" }, - * "claimsToSuppress": [ "string", "string" ] - * }, - * "accessTokenGeneration": { - * "claimsToAddOrOverride": { "string": "string" }, - * "claimsToSuppress": [ "string", "string" ], - * "scopesToAdd": [ "string", "string" ], - * "scopesToSuppress": [ "string", "string" ] - * }, - * "groupOverrideDetails": { - * "groupsToOverride": [ "string", "string" ], - * "iamRolesToOverride": [ "string", "string" ], - * "preferredRole": "string" - * } - * } - * } + * }, + * "response": {} * } * ``` - * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html} + * + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html | Amazon Cognito Developer Guide} */ -export const PreTokenGenerationTriggerSchemaV2 = z.object({ - request: z.object({ - userAttributes: z.record(z.string(), z.string()), - scopes: z.array(z.string()), - groupConfiguration: z.object({ - groupsToOverride: z.array(z.string()), - iamRolesToOverride: z.array(z.string()), - preferredRole: z.string(), - }), - clientMetadata: z.record(z.string(), z.string()), - }), - response: z.object({ - claimsAndScopeOverrideDetails: z.object({ - idTokenGeneration: z.object({ - claimsToAddOrOverride: z.record(z.any()), - claimsToSuppress: z.array(z.string()), - }), - accessTokenGeneration: z.object({ - claimsToAddOrOverride: z.record(z.any()), - claimsToSuppress: z.array(z.string()), - scopesToAdd: z.array(z.string()), - scopesToSuppress: z.array(z.string()), - }), - groupOverrideDetails: z.object({ - groupsToOverride: z.array(z.string()), - iamRolesToOverride: z.array(z.string()), - preferredRole: z.string(), - }), - }), +const PreTokenGenerationTriggerSchemaV2AndV3 = CognitoTriggerBaseSchema.extend({ + request: PreTokenGenerationTriggerRequestSchema.extend({ + scopes: z.array(z.string()).optional(), }), }); @@ -278,38 +283,47 @@ export const PreTokenGenerationTriggerSchemaV2 = z.object({ * @example * ```json * { - * "userName": "string", + * "version": "1", + * "triggerSource": "UserMigration_Authentication", + * "region": "us-east-1", + * "userPoolId": "us-east-1_ABC123", + * "userName": "johndoe", + * "callerContext": { + * "awsSdkVersion": "2.814.0", + * "clientId": "client123" + * }, * "request": { * "password": "string", * "validationData": { "key": "value" }, * "clientMetadata": { "key": "value" } * }, * "response": { - * "userAttributes": { "key": "value" }, - * "finalUserStatus": "string", - * "messageAction": "string", - * "desiredDeliveryMediums": [ "string" ], - * "forceAliasCreation": true, - * "enableSMSMFA": true + * "userAttributes": null, + * "finalUserStatus": null, + * "messageAction": null, + * "desiredDeliveryMediums": null, + * "forceAliasCreation": null, + * "enableSMSMFA": null * } * } * ``` - * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-migrate-user.html} + * + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-migrate-user.html | Amazon Cognito Developer Guide} */ -export const MigrateUserTriggerSchema = z.object({ +const MigrateUserTriggerSchema = CognitoTriggerBaseSchema.extend({ userName: z.string(), request: z.object({ password: z.string(), - validationData: z.record(z.string(), z.string()), - clientMetadata: z.record(z.string(), z.string()), + validationData: z.record(z.string(), z.string()).optional(), + clientMetadata: z.record(z.string(), z.string()).optional(), }), response: z.object({ - userAttributes: z.record(z.string(), z.string()), - finalUserStatus: z.string(), - messageAction: z.string(), - desiredDeliveryMediums: z.array(z.string()), - forceAliasCreation: z.boolean(), - enableSMSMFA: z.boolean(), + userAttributes: z.record(z.string(), z.string()).nullable(), + finalUserStatus: z.string().nullable(), + messageAction: z.string().nullable(), + desiredDeliveryMediums: z.array(z.string()).nullable(), + forceAliasCreation: z.boolean().nullable(), + enableSMSMFA: z.boolean().nullable(), }), }); @@ -319,32 +333,47 @@ export const MigrateUserTriggerSchema = z.object({ * @example * ```json * { + * "version": "1", + * "triggerSource": "CustomMessage_SignUp", + * "region": "us-east-1", + * "userPoolId": "us-east-1_ABC123", + * "userName": "johndoe", + * "callerContext": { + * "awsSdkVersion": "2.814.0", + * "clientId": "client123" + * }, * "request": { - * "userAttributes": { "string": "string", ... }, - * "codeParameter": "####", + * "userAttributes": { + * "email": "user@example.com", + * "name": "John Doe" + * }, + * "codeParameter": "{####}", * "usernameParameter": "string", - * "clientMetadata": { "string": "string", ... } + * "linkParameter": "string", + * "usernameParameter": null * }, * "response": { - * "smsMessage": "string", - * "emailMessage": "string", - * "emailSubject": "string" + * "smsMessage": null, + * "emailMessage": null, + * "emailSubject": null, * } * } * ``` - * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-message.html} + * + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-message.html | Amazon Cognito Developer Guide} */ -export const CustomMessageTriggerSchema = z.object({ +const CustomMessageTriggerSchema = CognitoTriggerBaseSchema.extend({ request: z.object({ userAttributes: z.record(z.string(), z.string()), codeParameter: z.string(), - usernameParameter: z.string(), + linkParameter: z.string().nullable(), + usernameParameter: z.string().nullable(), clientMetadata: z.record(z.string(), z.string()).optional(), }), response: z.object({ - smsMessage: z.string(), - emailMessage: z.string(), - emailSubject: z.string(), + smsMessage: z.string().nullable(), + emailMessage: z.string().nullable(), + emailSubject: z.string().nullable(), }), }); @@ -354,25 +383,35 @@ export const CustomMessageTriggerSchema = z.object({ * @example * ```json * { + * "version": "1", + * "triggerSource": "CustomEmailSender_SignUp", + * "region": "us-east-1", + * "userPoolId": "us-east-1_ABC123", + * "userName": "johndoe", + * "callerContext": { + * "awsSdkVersion": "2.814.0", + * "clientId": "client123" + * }, * "request": { * "type": "customEmailSenderRequestV1", * "code": "string", - * "clientMetadata": { "string": "string", ... }, - * "userAttributes": { "string": "string", ... } + * "clientMetadata": { "string": "string" }, + * "userAttributes": { "string": "string" } * }, * "response": {} * } * ``` - * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-email-sender.html} + * + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-email-sender.html | Amazon Cognito Developer Guide} */ -export const CustomEmailSenderTriggerSchema = z.object({ +const CustomEmailSenderTriggerSchema = CognitoTriggerBaseSchema.extend({ + triggerSource: z.literal('CustomEmailSender_SignUp'), request: z.object({ type: z.literal('customEmailSenderRequestV1'), code: z.string(), clientMetadata: z.record(z.string(), z.string()).optional(), userAttributes: z.record(z.string(), z.string()), }), - response: z.object({}), }); /** @@ -381,29 +420,54 @@ export const CustomEmailSenderTriggerSchema = z.object({ * @example * ```json * { + * "version": "1", + * "triggerSource": "CustomSMSSender_SignUp", + * "region": "us-east-1", + * "userPoolId": "us-east-1_ABC123", + * "userName": "johndoe", + * "callerContext": { + * "awsSdkVersion": "2.814.0", + * "clientId": "client123" + * }, * "request": { * "type": "customSMSSenderRequestV1", * "code": "string", - * "clientMetadata": { "string": "string", ... }, - * "userAttributes": { "string": "string", ... } + * "clientMetadata": { + * "string": "string" + * }, + * "userAttributes": { "string": "string" } * }, * "response": {} * } * ``` - * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-sms-sender.html} + * + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-sms-sender.html | Amazon Cognito Developer Guide} */ -export const CustomSMSSenderTriggerSchema = z.object({ +const CustomSMSSenderTriggerSchema = CognitoTriggerBaseSchema.extend({ + triggerSource: z.literal('CustomSMSSender_SignUp'), request: z.object({ type: z.literal('customSMSSenderRequestV1'), code: z.string(), clientMetadata: z.record(z.string(), z.string()).optional(), userAttributes: z.record(z.string(), z.string()), }), - response: z.object({}), }); +/** + * A zod schema for a Cognito Challenge Result. + */ const ChallengeResultSchema = z.object({ - challengeName: z.string(), + challengeName: z.union([ + z.literal('CUSTOM_CHALLENGE'), + z.literal('SRP_A'), + z.literal('PASSWORD_VERIFIER'), + z.literal('SMS_MFA'), + z.literal('EMAIL_OTP'), + z.literal('SOFTWARE_TOKEN_MFA'), + z.literal('DEVICE_SRP_AUTH'), + z.literal('DEVICE_PASSWORD_VERIFIER'), + z.literal('ADMIN_NO_SRP_AUTH'), + ]), challengeResult: z.boolean(), challengeMetadata: z.string().optional(), }); @@ -414,6 +478,15 @@ const ChallengeResultSchema = z.object({ * @example * ```json * { + * "version": "1", + * "triggerSource": "DefineAuthChallenge_Authentication", + * "region": "us-east-1", + * "userPoolId": "us-east-1_ABC123", + * "userName": "johndoe", + * "callerContext": { + * "awsSdkVersion": "2.814.0", + * "clientId": "client123" + * }, * "request": { * "userAttributes": { "email": "user@example.com", "name": "John Doe" }, * "session": [ @@ -433,19 +506,21 @@ const ChallengeResultSchema = z.object({ * } * } * ``` - * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-define-auth-challenge.html} + * + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-define-auth-challenge.html | Amazon Cognito Developer Guide} */ -export const DefineAuthChallengeSchema = z.object({ +const DefineAuthChallengeTriggerSchema = CognitoTriggerBaseSchema.extend({ + triggerSource: z.literal('DefineAuthChallenge_Authentication'), request: z.object({ userAttributes: z.record(z.string(), z.string()), - session: z.array(ChallengeResultSchema), + session: z.array(ChallengeResultSchema).min(1), clientMetadata: z.record(z.string(), z.string()).optional(), - userNotFound: z.boolean(), + userNotFound: z.boolean().optional(), }), response: z.object({ - challengeName: z.string(), - issueTokens: z.boolean(), - failAuthentication: z.boolean(), + challengeName: z.string().nullish(), + issueTokens: z.boolean().nullish(), + failAuthentication: z.boolean().nullish(), }), }); @@ -455,6 +530,15 @@ export const DefineAuthChallengeSchema = z.object({ * @example * ```json * { + * "version": "1", + * "triggerSource": "CreateAuthChallenge_Authentication", + * "region": "us-east-1", + * "userPoolId": "us-east-1_ABC123", + * "userName": "johndoe", + * "callerContext": { + * "awsSdkVersion": "2.814.0", + * "clientId": "client123" + * }, * "request": { * "userAttributes": { "email": "user@example.com", "name": "John Doe" }, * "challengeName": "CUSTOM_CHALLENGE", @@ -471,20 +555,22 @@ export const DefineAuthChallengeSchema = z.object({ * } * } * ``` - * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-create-auth-challenge.html} + * + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-create-auth-challenge.html | Amazon Cognito Developer Guide} */ -export const CreateAuthChallengeSchema = z.object({ +const CreateAuthChallengeTriggerSchema = CognitoTriggerBaseSchema.extend({ + triggerSource: z.literal('CreateAuthChallenge_Authentication'), request: z.object({ userAttributes: z.record(z.string(), z.string()), challengeName: z.string(), - session: z.array(ChallengeResultSchema), + session: z.array(ChallengeResultSchema).min(1), clientMetadata: z.record(z.string(), z.string()).optional(), - userNotFound: z.boolean(), + userNotFound: z.boolean().optional(), }), response: z.object({ - publicChallengeParameters: z.record(z.string()), - privateChallengeParameters: z.record(z.string()), - challengeMetadata: z.string(), + publicChallengeParameters: z.record(z.string()).nullish(), + privateChallengeParameters: z.record(z.string()).nullish(), + challengeMetadata: z.string().nullish(), }), }); @@ -494,6 +580,15 @@ export const CreateAuthChallengeSchema = z.object({ * @example * ```json * { + * "version": "1", + * "triggerSource": "VerifyAuthChallengeResponse_Authentication", + * "region": "us-east-1", + * "userPoolId": "us-east-1_ABC123", + * "userName": "johndoe", + * "callerContext": { + * "awsSdkVersion": "2.814.0", + * "clientId": "client123" + * }, * "request": { * "userAttributes": { "email": "user@example.com", "name": "John Doe" }, * "privateChallengeParameters": { "answer": "expectedAnswer" }, @@ -506,17 +601,39 @@ export const CreateAuthChallengeSchema = z.object({ * } * } * ``` - * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-verify-auth-challenge-response.html} + * + * @see {@link https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-verify-auth-challenge-response.html | Amazon Cognito Developer Guide} */ -export const VerifyAuthChallengeSchema = z.object({ +const VerifyAuthChallengeTriggerSchema = CognitoTriggerBaseSchema.extend({ + triggerSource: z.literal('VerifyAuthChallengeResponse_Authentication'), request: z.object({ userAttributes: z.record(z.string(), z.string()), privateChallengeParameters: z.record(z.string(), z.string()), challengeAnswer: z.string(), clientMetadata: z.record(z.string(), z.string()).optional(), - userNotFound: z.boolean(), + userNotFound: z.boolean().optional(), }), response: z.object({ answerCorrect: z.boolean(), }), }); + +export { + CognitoTriggerBaseSchema, + PreSignupTriggerSchema, + PostConfirmationTriggerSchema, + PreAuthenticationTriggerSchema, + PostAuthenticationTriggerSchema, + MigrateUserTriggerSchema, + CustomMessageTriggerSchema, + CustomEmailSenderTriggerSchema, + CustomSMSSenderTriggerSchema, + ChallengeResultSchema, + DefineAuthChallengeTriggerSchema, + CreateAuthChallengeTriggerSchema, + VerifyAuthChallengeTriggerSchema, + PreTokenGenerationTriggerSchemaV1, + PreTokenGenerationTriggerSchemaV2AndV3, + PreTokenGenerationTriggerGroupConfigurationSchema, + PreTokenGenerationTriggerRequestSchema, +}; diff --git a/packages/parser/tests/events/cognito/base.json b/packages/parser/tests/events/cognito/base.json new file mode 100644 index 0000000000..0ea414d08e --- /dev/null +++ b/packages/parser/tests/events/cognito/base.json @@ -0,0 +1,13 @@ +{ + "version": "1", + "region": "eu-west-1", + "userPoolId": "eu-west-1_bbYLzC8rt", + "userName": "example@email.com", + "callerContext": { + "awsSdkVersion": "aws-sdk-unknown-unknown", + "clientId": "4ma39kqn9j6ncsimddqs13sgok" + }, + "triggerSource": "", + "request": {}, + "response": {} +} \ No newline at end of file diff --git a/packages/parser/tests/unit/schema/cognito.test.ts b/packages/parser/tests/unit/schema/cognito.test.ts index fc75576775..050b8aa233 100644 --- a/packages/parser/tests/unit/schema/cognito.test.ts +++ b/packages/parser/tests/unit/schema/cognito.test.ts @@ -1,472 +1,283 @@ import { describe, expect, it } from 'vitest'; import { - CreateAuthChallengeSchema, + CreateAuthChallengeTriggerSchema, CustomEmailSenderTriggerSchema, CustomMessageTriggerSchema, CustomSMSSenderTriggerSchema, - DefineAuthChallengeSchema, + DefineAuthChallengeTriggerSchema, MigrateUserTriggerSchema, PostAuthenticationTriggerSchema, PostConfirmationTriggerSchema, PreAuthenticationTriggerSchema, PreSignupTriggerSchema, PreTokenGenerationTriggerSchemaV1, - PreTokenGenerationTriggerSchemaV2, - VerifyAuthChallengeSchema, + VerifyAuthChallengeTriggerSchema, } from '../../../src/schemas/cognito.js'; -import { omit } from '../helpers/utils.js'; - -describe('Schema: Cognito PreSignupTrigger', () => { - // Prepare - const basePreSignupEvent = { - version: '1', - region: 'us-east-1', - userPoolId: 'us-east-1_ABC123', - userName: 'johndoe', - callerContext: { - awsSdkVersion: '2.814.0', - clientId: 'client123', - }, - triggerSource: 'PreSignUp_SignUp', - request: { - userAttributes: { - email: 'johndoe@example.com', - name: 'John Doe', - }, +import { getTestEvent } from '../helpers/utils.js'; + +describe('Schemas: Cognito User Pool', () => { + const baseEvent = getTestEvent({ + eventsPath: 'cognito', + filename: 'base', + }); + + it('parses a valid pre-signup event', () => { + // Prepare + const event = structuredClone(baseEvent); + event.triggerSource = 'PreSignUp_SignUp'; + event.request = { + userAttributes: {}, validationData: null, - clientMetadata: { - someKey: 'someValue', - }, - }, - response: { - autoConfirmUser: true, + }; + event.response = { + autoConfirmUser: false, autoVerifyEmail: false, autoVerifyPhone: false, - }, - }; + }; - it('parses a valid pre-signup event', () => { - // Prepare - const event = structuredClone(basePreSignupEvent); // Act - const result = PreSignupTriggerSchema.safeParse(event); + const result = PreSignupTriggerSchema.parse(event); + // Assess - expect(result.success).toBe(true); - if (result.success) { - expect(result.data.userName).toEqual('johndoe'); - } + expect(result).toEqual(event); }); it('throws if the pre-signup event is missing a required field', () => { // Prepare - const event = omit(['userName'], structuredClone(basePreSignupEvent)); + const event = structuredClone(baseEvent); + // Act & Assess expect(() => PreSignupTriggerSchema.parse(event)).toThrow(); }); -}); -describe('Schema: Cognito PostConfirmationTrigger', () => { - // Prepare - const basePostConfirmationEvent = { - request: { + it('parses a valid post-confirmation event', () => { + // Prepare + const event = structuredClone(baseEvent); + event.triggerSource = 'PostConfirmation_ConfirmSignUp'; + event.request = { userAttributes: { - email: 'user@example.com', - name: 'John Doe', - }, - clientMetadata: { - customKey: 'customValue', + sub: '42051434-5091-70ec-4b71-7c26db407ea4', + 'cognito:user_status': 'CONFIRMED', }, - }, - response: {}, - }; + }; - it('parses a valid post-confirmation event', () => { - // Prepare - const event = structuredClone(basePostConfirmationEvent); // Act - const result = PostConfirmationTriggerSchema.safeParse(event); + const result = PostConfirmationTriggerSchema.parse(event); + // Assess - expect(result.success).toBe(true); + expect(result).toEqual(event); }); it('throws if the post-confirmation event is missing a required field', () => { // Prepare - const event = { - ...basePostConfirmationEvent, - request: omit( - ['userAttributes'], - structuredClone(basePostConfirmationEvent.request) - ), - }; + const event = structuredClone(baseEvent); + // Act & Assess expect(() => PostConfirmationTriggerSchema.parse(event)).toThrow(); }); -}); - -describe('Schema: Cognito PreAuthenticationTrigger', () => { - // Prepare - const basePreAuthEvent = { - request: { - userAttributes: { - email: 'user@example.com', - name: 'John Doe', - }, - validationData: { - someKey: 'someValue', - }, - userNotFound: false, - }, - response: {}, - }; - - it('parses a valid pre-authentication event', () => { - // Prepare - const event = structuredClone(basePreAuthEvent); - // Act - const result = PreAuthenticationTriggerSchema.safeParse(event); - // Assess - expect(result.success).toBe(true); - }); - it('throws if the pre-authentication event is missing a required field', () => { + it('parses a valid post-authentication event', () => { // Prepare - const event = { - ...basePreAuthEvent, - request: omit( - ['userAttributes'], - structuredClone(basePreAuthEvent.request) - ), - }; - // Act & Assess - expect(() => PreAuthenticationTriggerSchema.parse(event)).toThrow(); - }); -}); - -describe('Schema: Cognito PostAuthenticationTrigger', () => { - // Prepare - const basePostAuthEvent = { - request: { + const event = structuredClone(baseEvent); + event.triggerSource = 'PostAuthentication_Authentication'; + event.request = { userAttributes: { - email: 'user@example.com', - name: 'John Doe', + sub: '42051434-5091-70ec-4b71-7c26db407ea4', + 'cognito:user_status': 'CONFIRMED', }, - newDeviceUsed: true, + newDeviceUsed: false, clientMetadata: { - customKey: 'customValue', + someKey: 'someValue', }, - }, - response: {}, - }; + }; - it('parses a valid post-authentication event', () => { - // Prepare - const event = structuredClone(basePostAuthEvent); // Act - const result = PostAuthenticationTriggerSchema.safeParse(event); + const result = PostAuthenticationTriggerSchema.parse(event); + // Assess - expect(result.success).toBe(true); + expect(result).toEqual(event); }); it('throws if the post-authentication event is missing a required field', () => { // Prepare - const event = { - ...basePostAuthEvent, - request: omit( - ['newDeviceUsed'], - structuredClone(basePostAuthEvent.request) - ), - }; + const event = structuredClone(baseEvent); + // Act & Assess expect(() => PostAuthenticationTriggerSchema.parse(event)).toThrow(); }); -}); - -describe('Schema: Cognito PreTokenGenerationTrigger V1', () => { - // Prepare - const basePreTokenGenEventV1 = { - request: { - userAttributes: { - email: 'user@example.com', - name: 'John Doe', - }, - groupConfiguration: { - groupsToOverride: ['group1', 'group2'], - iamRolesToOverride: ['role1', 'role2'], - preferredRole: 'role1', - }, - clientMetadata: { - key1: 'value1', - }, - }, - response: { - claimsOverrideDetails: { - claimsToAddOrOverride: { - customClaim: 'customValue', - }, - claimsToSuppress: ['email'], - groupOverrideDetails: { - groupsToOverride: ['newGroup1', 'newGroup2'], - iamRolesToOverride: ['newRole1', 'newRole2'], - preferredRole: 'newRole1', - }, - }, - }, - }; - - it('parses a valid pre-token generation V1 event', () => { - // Prepare - const event = structuredClone(basePreTokenGenEventV1); - // Act - const result = PreTokenGenerationTriggerSchemaV1.safeParse(event); - // Assess - expect(result.success).toBe(true); - }); - it('throws if the pre-token generation V1 event is missing a required field', () => { + it('parses a valid pre-authentication event', () => { // Prepare - const event = { - ...basePreTokenGenEventV1, - request: omit( - ['groupConfiguration'], - structuredClone(basePreTokenGenEventV1.request) - ), - }; - // Act & Assess - expect(() => PreTokenGenerationTriggerSchemaV1.parse(event)).toThrow(); - }); -}); - -describe('Schema: Cognito PreTokenGenerationTrigger V2', () => { - // Prepare - const basePreTokenGenEventV2 = { - request: { + const event = structuredClone(baseEvent); + event.triggerSource = 'PreAuthentication_Authentication'; + event.request = { userAttributes: { - email: 'user@example.com', - name: 'John Doe', - }, - scopes: ['openid', 'email'], - groupConfiguration: { - groupsToOverride: ['group1', 'group2'], - iamRolesToOverride: ['role1', 'role2'], - preferredRole: 'role1', - }, - clientMetadata: { - key1: 'value1', - }, - }, - response: { - claimsAndScopeOverrideDetails: { - idTokenGeneration: { - claimsToAddOrOverride: { - customClaim: 'customValue', - }, - claimsToSuppress: ['email'], - }, - accessTokenGeneration: { - claimsToAddOrOverride: { - customClaim: 'customValue', - }, - claimsToSuppress: ['email'], - scopesToAdd: ['openid', 'profile'], - scopesToSuppress: ['phone'], - }, - groupOverrideDetails: { - groupsToOverride: ['newGroup1', 'newGroup2'], - iamRolesToOverride: ['newRole1', 'newRole2'], - preferredRole: 'newRole1', - }, + sub: '42051434-5091-70ec-4b71-7c26db407ea4', + 'cognito:user_status': 'CONFIRMED', }, - }, - }; + userNotFound: false, + }; - it('parses a valid pre-token generation V2 event', () => { - // Prepare - const event = structuredClone(basePreTokenGenEventV2); // Act - const result = PreTokenGenerationTriggerSchemaV2.safeParse(event); + const result = PreAuthenticationTriggerSchema.parse(event); + // Assess - expect(result.success).toBe(true); + expect(result).toEqual(event); }); - it('throws if the pre-token generation V2 event is missing a required field', () => { + it('throws if the pre-authentication event is missing a required field', () => { // Prepare - const event = { - ...basePreTokenGenEventV2, - request: omit( - ['scopes'], - structuredClone(basePreTokenGenEventV2.request) - ), - }; + const event = structuredClone(baseEvent); + // Act & Assess - expect(() => PreTokenGenerationTriggerSchemaV2.parse(event)).toThrow(); + expect(() => PreAuthenticationTriggerSchema.parse(event)).toThrow(); }); -}); -describe('Schema: Cognito MigrateUserTrigger', () => { - // Prepare - const baseMigrateUserEvent = { - userName: 'testuser', - request: { - password: 'dummyPass123!', + it('parses a valid migrate user event', () => { + // Prepare + const event = structuredClone(baseEvent); + event.triggerSource = 'UserMigration_Authentication'; + event.request = { + password: 'string', validationData: { key1: 'value1', }, clientMetadata: { key2: 'value2', }, - }, - response: { - userAttributes: { - email: 'testuser@example.com', - name: 'Test User', - }, - finalUserStatus: 'CONFIRMED', - messageAction: 'SUPPRESS', - desiredDeliveryMediums: ['EMAIL'], - forceAliasCreation: true, - enableSMSMFA: false, - }, - }; + }; + event.response = { + userAttributes: null, + forceAliasCreation: null, + enableSMSMFA: null, + finalUserStatus: null, + messageAction: null, + desiredDeliveryMediums: null, + }; - it('parses a valid migrate user event', () => { - // Prepare - const event = structuredClone(baseMigrateUserEvent); // Act - const result = MigrateUserTriggerSchema.safeParse(event); + const result = MigrateUserTriggerSchema.parse(event); + // Assess - expect(result.success).toBe(true); - if (result.success) { - expect(result.data.userName).toEqual('testuser'); - } + expect(result).toEqual(event); }); it('throws if the migrate user event is missing a required field', () => { // Prepare - const event = omit(['userName'], structuredClone(baseMigrateUserEvent)); + const event = structuredClone(baseEvent); + // Act & Assess expect(() => MigrateUserTriggerSchema.parse(event)).toThrow(); }); -}); - -describe('Schema: Cognito CustomMessageTrigger', () => { - // Prepare - const baseCustomMessageEvent = { - request: { - userAttributes: { email: 'test@example.com', name: 'Test User' }, - codeParameter: '####', - usernameParameter: 'TestUser', - clientMetadata: { key: 'value' }, - }, - response: { - smsMessage: 'Your code is ####', - emailMessage: 'Your verification code is ####', - emailSubject: 'Verification', - }, - }; it('parses a valid custom message event', () => { // Prepare - const event = structuredClone(baseCustomMessageEvent); + const event = structuredClone(baseEvent); + event.triggerSource = 'CustomMessage_SignUp'; + event.request = { + userAttributes: { + sub: '42051434-5091-70ec-4b71-7c26db407ea4', + 'cognito:user_status': 'CONFIRMED', + }, + codeParameter: 'string', + usernameParameter: 'string', + clientMetadata: { + key1: 'value1', + }, + linkParameter: 'string', + }; + event.response = { + smsMessage: null, + emailMessage: null, + emailSubject: null, + }; + // Act - const result = CustomMessageTriggerSchema.safeParse(event); + const result = CustomMessageTriggerSchema.parse(event); + // Assess - expect(result.success).toBe(true); + expect(result).toEqual(event); }); it('throws if the custom message event is missing a required field', () => { // Prepare - const event = { - ...baseCustomMessageEvent, - request: omit( - ['codeParameter'], - structuredClone(baseCustomMessageEvent.request) - ), - }; + const event = structuredClone(baseEvent); + // Act & Assess expect(() => CustomMessageTriggerSchema.parse(event)).toThrow(); }); -}); -describe('Schema: Cognito CustomEmailSenderTrigger', () => { - // Prepare - const baseCustomEmailSenderEvent = { - request: { + it('parses a valid custom message event with custom email sender', () => { + // Prepare + const event = structuredClone(baseEvent); + event.triggerSource = 'CustomEmailSender_SignUp'; + event.request = { type: 'customEmailSenderRequestV1', - code: 'encryptedCode', - clientMetadata: { key: 'value' }, - userAttributes: { email: 'test@example.com', name: 'Test User' }, - }, - response: {}, - }; + code: 'string', + clientMetadata: { + key1: 'value1', + }, + userAttributes: { + sub: '42051434-5091-70ec-4b71-7c26db407ea4', + 'cognito:user_status': 'CONFIRMED', + }, + }; - it('parses a valid custom email sender event', () => { - // Prepare - const event = structuredClone(baseCustomEmailSenderEvent); // Act - const result = CustomEmailSenderTriggerSchema.safeParse(event); + const result = CustomEmailSenderTriggerSchema.parse(event); + // Assess - expect(result.success).toBe(true); - if (result.success) { - expect(result.data.response).toEqual({}); - } + expect(result).toEqual(event); }); - it('throws if the custom email sender event is missing a required field', () => { + it('throws if the custom message event with custom email sender is missing a required field', () => { // Prepare - const event = { - ...baseCustomEmailSenderEvent, - request: omit( - ['code'], - structuredClone(baseCustomEmailSenderEvent.request) - ), - }; + const event = structuredClone(baseEvent); + // Act & Assess expect(() => CustomEmailSenderTriggerSchema.parse(event)).toThrow(); }); -}); -describe('Schema: Cognito CustomSMSSenderTrigger', () => { - // Prepare - const baseCustomSMSSenderEvent = { - request: { + it('parses a valid custom message event with custom SMS sender', () => { + // Prepare + const event = structuredClone(baseEvent); + event.triggerSource = 'CustomSMSSender_SignUp'; + event.request = { type: 'customSMSSenderRequestV1', - code: 'encryptedCode', - clientMetadata: { key: 'value' }, + code: 'string', + clientMetadata: { + key1: 'value1', + }, userAttributes: { - phone_number: '+1234567890', - email: 'test@example.com', + sub: '42051434-5091-70ec-4b71-7c26db407ea4', + 'cognito:user_status': 'CONFIRMED', }, - }, - response: {}, - }; + }; - it('parses a valid custom SMS sender event', () => { - // Prepare - const event = structuredClone(baseCustomSMSSenderEvent); // Act - const result = CustomSMSSenderTriggerSchema.safeParse(event); + const result = CustomSMSSenderTriggerSchema.parse(event); + // Assess - expect(result.success).toBe(true); + expect(result).toEqual(event); }); - it('throws if the custom SMS sender event is missing a required field', () => { + it('throws if the custom message event with custom SMS sender is missing a required field', () => { // Prepare - const event = { - ...baseCustomSMSSenderEvent, - request: omit( - ['code'], - structuredClone(baseCustomSMSSenderEvent.request) - ), - }; + const event = structuredClone(baseEvent); + // Act & Assess expect(() => CustomSMSSenderTriggerSchema.parse(event)).toThrow(); }); -}); -describe('Schema: Cognito DefineAuthChallenge', () => { - const baseDefineAuthChallengeEvent = { - request: { - userAttributes: { email: 'user@example.com', name: 'John Doe' }, + it('parses a valid define auth challenge event', () => { + // Prepare + const event = structuredClone(baseEvent); + event.triggerSource = 'DefineAuthChallenge_Authentication'; + event.request = { + userAttributes: { + sub: '42051434-5091-70ec-4b71-7c26db407ea4', + 'cognito:user_status': 'CONFIRMED', + }, session: [ { challengeName: 'SRP_A', @@ -476,37 +287,32 @@ describe('Schema: Cognito DefineAuthChallenge', () => { ], clientMetadata: { key: 'value' }, userNotFound: false, - }, - response: { - challengeName: 'PASSWORD_VERIFIER', - issueTokens: false, - failAuthentication: false, - }, - }; + }; - it('parses a valid define auth challenge event', () => { - const event = structuredClone(baseDefineAuthChallengeEvent); - const result = DefineAuthChallengeSchema.safeParse(event); - expect(result.success).toBe(true); + // Act + const result = DefineAuthChallengeTriggerSchema.parse(event); + + // Assess + expect(result).toEqual(event); }); it('throws if the define auth challenge event is missing a required field', () => { - const event = { - ...baseDefineAuthChallengeEvent, - request: omit( - ['userAttributes'], - structuredClone(baseDefineAuthChallengeEvent.request) - ), - }; - expect(() => DefineAuthChallengeSchema.parse(event)).toThrow(); + // Prepare + const event = structuredClone(baseEvent); + + // Act & Assess + expect(() => DefineAuthChallengeTriggerSchema.parse(event)).toThrow(); }); -}); -describe('Schema: Cognito CreateAuthChallenge', () => { - // Prepare - const baseCreateAuthChallengeEvent = { - request: { - userAttributes: { email: 'user@example.com', name: 'John Doe' }, + it('parses a valid create auth challenge event', () => { + // Prepare + const event = structuredClone(baseEvent); + event.triggerSource = 'CreateAuthChallenge_Authentication'; + event.request = { + userAttributes: { + sub: '42051434-5091-70ec-4b71-7c26db407ea4', + 'cognito:user_status': 'CONFIRMED', + }, challengeName: 'CUSTOM_CHALLENGE', session: [ { @@ -517,79 +323,84 @@ describe('Schema: Cognito CreateAuthChallenge', () => { ], clientMetadata: { key: 'value' }, userNotFound: false, - }, - response: { - publicChallengeParameters: { captchaUrl: 'url/123.jpg' }, - privateChallengeParameters: { answer: '5' }, - challengeMetadata: 'custom metadata', - }, - }; + }; - it('parses a valid create auth challenge event', () => { - // Prepare - const event = structuredClone(baseCreateAuthChallengeEvent); // Act - const result = CreateAuthChallengeSchema.safeParse(event); + const result = CreateAuthChallengeTriggerSchema.parse(event); + // Assess - expect(result.success).toBe(true); - if (result.success) { - expect(result.data.response.publicChallengeParameters.captchaUrl).toEqual( - 'url/123.jpg' - ); - } + expect(result).toEqual(event); }); it('throws if the create auth challenge event is missing a required field', () => { // Prepare - const event = { - ...baseCreateAuthChallengeEvent, - request: omit( - ['challengeName'], - structuredClone(baseCreateAuthChallengeEvent.request) - ), - }; + const event = structuredClone(baseEvent); + // Act & Assess - expect(() => CreateAuthChallengeSchema.parse(event)).toThrow(); + expect(() => CreateAuthChallengeTriggerSchema.parse(event)).toThrow(); }); -}); -describe('Schema: Cognito VerifyAuthChallenge', () => { - // Prepare - const baseVerifyAuthChallengeEvent = { - request: { - userAttributes: { email: 'user@example.com', name: 'John Doe' }, + it('parses a valid verify auth challenge event', () => { + // Prepare + const event = structuredClone(baseEvent); + event.triggerSource = 'VerifyAuthChallengeResponse_Authentication'; + event.request = { + userAttributes: { + sub: '42051434-5091-70ec-4b71-7c26db407ea4', + 'cognito:user_status': 'CONFIRMED', + }, privateChallengeParameters: { answer: 'expectedAnswer' }, challengeAnswer: 'expectedAnswer', clientMetadata: { key: 'value' }, userNotFound: false, - }, - response: { + }; + event.response = { answerCorrect: true, - }, - }; + }; - it('parses a valid verify auth challenge event', () => { - // Prepare - const event = structuredClone(baseVerifyAuthChallengeEvent); // Act - const result = VerifyAuthChallengeSchema.safeParse(event); + const result = VerifyAuthChallengeTriggerSchema.parse(event); + // Assess - expect(result.success).toBe(true); - if (result.success) { - expect(result.data.response.answerCorrect).toBe(true); - } + expect(result).toEqual(event); }); it('throws if the verify auth challenge event is missing a required field', () => { // Prepare - const event = { - ...baseVerifyAuthChallengeEvent, - request: omit( - ['privateChallengeParameters'], - structuredClone(baseVerifyAuthChallengeEvent.request) - ), + const event = structuredClone(baseEvent); + + // Act & Assess + expect(() => VerifyAuthChallengeTriggerSchema.parse(event)).toThrow(); + }); + + it('parses a valid pre-token generation event v1', () => { + // Prepare + const event = structuredClone(baseEvent); + event.request = { + userAttributes: { + sub: '42051434-5091-70ec-4b71-7c26db407ea4', + 'cognito:user_status': 'CONFIRMED', + }, + groupConfiguration: { + groupsToOverride: ['group1', 'group2'], + iamRolesToOverride: ['role1', 'role2'], + preferredRole: 'role1', + }, + clientMetadata: { key: 'value' }, }; + + // Act + const result = PreTokenGenerationTriggerSchemaV1.parse(event); + + // Assess + expect(result).toEqual(event); + }); + + it('throws if the pre-token generation event v1 is missing a required field', () => { + // Prepare + const event = structuredClone(baseEvent); + // Act & Assess - expect(() => VerifyAuthChallengeSchema.parse(event)).toThrow(); + expect(() => PreTokenGenerationTriggerSchemaV1.parse(event)).toThrow(); }); }); From eec88cece5e0f0cee1c88662f68238aec609324f Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 1 Apr 2025 17:58:01 +0200 Subject: [PATCH 7/7] chore: fix exports --- packages/parser/src/schemas/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/parser/src/schemas/index.ts b/packages/parser/src/schemas/index.ts index 3ba962bca1..9687e65064 100644 --- a/packages/parser/src/schemas/index.ts +++ b/packages/parser/src/schemas/index.ts @@ -31,14 +31,14 @@ export { PreAuthenticationTriggerSchema, PostAuthenticationTriggerSchema, PreTokenGenerationTriggerSchemaV1, - PreTokenGenerationTriggerSchemaV2, + PreTokenGenerationTriggerSchemaV2AndV3, MigrateUserTriggerSchema, CustomMessageTriggerSchema, CustomEmailSenderTriggerSchema, CustomSMSSenderTriggerSchema, - DefineAuthChallengeSchema, - CreateAuthChallengeSchema, - VerifyAuthChallengeSchema, + DefineAuthChallengeTriggerSchema, + CreateAuthChallengeTriggerSchema, + VerifyAuthChallengeTriggerSchema, } from './cognito.js'; export { DynamoDBStreamSchema,