From 7b2e6584d17ba4b05f7c6b4dd139ed98fb58ef68 Mon Sep 17 00:00:00 2001 From: Gavin Zhang Date: Tue, 23 Jan 2024 12:33:20 -0800 Subject: [PATCH 1/6] fix(cognito): allow custom email msg placeholder --- packages/aws-cdk-lib/aws-cognito/README.md | 19 ++++++++++++++ .../aws-cdk-lib/aws-cognito/lib/user-pool.ts | 3 ++- .../aws-cognito/test/user-pool.test.ts | 25 ++++++++++++++++++- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk-lib/aws-cognito/README.md b/packages/aws-cdk-lib/aws-cognito/README.md index 8616cb0b929ab..f058bdbd48ebf 100644 --- a/packages/aws-cdk-lib/aws-cognito/README.md +++ b/packages/aws-cdk-lib/aws-cognito/README.md @@ -79,6 +79,11 @@ Users can either be signed up by the app's administrators or can sign themselves account needs to be confirmed. Cognito provides several ways to sign users up and confirm their accounts. Learn more about [user sign up here](https://docs.aws.amazon.com/cognito/latest/developerguide/signing-up-users-in-your-app.html). +To verify the email address of a user in your user pool with Amazon Cognito, you can send the user an email message +with a link that they can select, or you can send them a code that they can enter. + +#### Code Verification + When a user signs up, email and SMS messages are used to verify their account and contact methods. The following code snippet configures a user pool with properties relevant to these verification messages - @@ -113,6 +118,20 @@ new cognito.UserPool(this, 'myuserpool', { }); ``` +#### Link Verification +Alternatively, users can use link as a verification method. The following code snippet configures a user pool with +properties relevant to these verification messages and link verification method. + +```ts +new UserPool(stack, 'myuserpool', { + userVerification: { + emailStyle: VerificationEmailStyle.LINK, + emailSubject: 'Invite to join our awesome app!', + emailBody: 'You have been invited to join our awesome app! {##Verify Your Email##}', + }, +}); +``` + All email subjects, bodies and SMS messages for both invitation and verification support Cognito's message templating. Learn more about [message templates here](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-settings-message-templates.html). diff --git a/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts b/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts index aca961028a79a..0cf936094b946 100644 --- a/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts +++ b/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts @@ -1047,6 +1047,7 @@ export class UserPool extends UserPoolBase { private verificationMessageConfiguration(props: UserPoolProps): CfnUserPool.VerificationMessageTemplateProperty { const CODE_TEMPLATE = '{####}'; const VERIFY_EMAIL_TEMPLATE = '{##Verify Email##}'; + const VERIFY_EMAIL_REGEX = /{##.*##}/g; const emailStyle = props.userVerification?.emailStyle ?? VerificationEmailStyle.CODE; const emailSubject = props.userVerification?.emailSubject ?? 'Verify your new account'; @@ -1069,7 +1070,7 @@ export class UserPool extends UserPoolBase { } else { const emailMessage = props.userVerification?.emailBody ?? `Verify your account by clicking on ${VERIFY_EMAIL_TEMPLATE}`; - if (!Token.isUnresolved(emailMessage) && emailMessage.indexOf(VERIFY_EMAIL_TEMPLATE) < 0) { + if (!Token.isUnresolved(emailMessage) && !VERIFY_EMAIL_REGEX.test(emailMessage)) { throw new Error(`Verification email body must contain the template string '${VERIFY_EMAIL_TEMPLATE}'`); } return { diff --git a/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts b/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts index af482b5abf1ab..e238fa22cb133 100644 --- a/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts +++ b/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts @@ -161,7 +161,7 @@ describe('User Pool', () => { emailStyle: VerificationEmailStyle.LINK, emailBody: 'invalid email body {####}', }, - })).toThrow(/Verification email body/); + })).not.toThrow(); expect(() => new UserPool(stack, 'Pool6', { userVerification: { @@ -355,6 +355,29 @@ describe('User Pool', () => { }); }); + test("custom email body with non 'Verify Email' placeholder text", () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'Pool', { + userVerification: { + emailStyle: VerificationEmailStyle.LINK, + emailSubject: 'Please verify your e-mail', + emailBody: '

Hello world {##Custom Text##}

', + }, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Cognito::UserPool', { + VerificationMessageTemplate: { + DefaultEmailOption: 'CONFIRM_WITH_LINK', + EmailMessageByLink: '

Hello world {##Custom Text##}

', + EmailSubjectByLink: 'Please verify your e-mail', + }, + }); + }); + test('custom sender lambda triggers via properties are correctly configured', () => { // GIVEN const stack = new Stack(); From a29c92f782cfed1a127edf8528fb49cadcef76a3 Mon Sep 17 00:00:00 2001 From: Gavin Zhang Date: Tue, 23 Jan 2024 12:56:47 -0800 Subject: [PATCH 2/6] add integ test Update readme Update readme use lazy qualifier to avoid Polynomial regular expression --- .../cdk.out | 1 + ...r-pool-link-custom-placeholder.assets.json | 19 +++ ...pool-link-custom-placeholder.template.json | 76 +++++++++ .../integ.json | 19 +++ .../manifest.json | 119 +++++++++++++ .../tree.json | 156 ++++++++++++++++++ ...efaultTestDeployAssert5C199314.assets.json | 19 +++ ...aultTestDeployAssert5C199314.template.json | 36 ++++ ...integ.user-pool-link-custom-placeholder.ts | 32 ++++ packages/aws-cdk-lib/aws-cognito/README.md | 4 +- .../aws-cdk-lib/aws-cognito/lib/user-pool.ts | 2 +- 11 files changed, 480 insertions(+), 3 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/integ-user-pool-link-custom-placeholder.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/integ-user-pool-link-custom-placeholder.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/integ-user-pool-link-custom-placeholder.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/integ-user-pool-link-custom-placeholder.assets.json new file mode 100644 index 0000000000000..cb5c759a25aec --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/integ-user-pool-link-custom-placeholder.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "8933ce2d15790f339b208cf26b074a443813cb433115af260ab927c97e241c47": { + "source": { + "path": "integ-user-pool-link-custom-placeholder.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "8933ce2d15790f339b208cf26b074a443813cb433115af260ab927c97e241c47.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/integ-user-pool-link-custom-placeholder.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/integ-user-pool-link-custom-placeholder.template.json new file mode 100644 index 0000000000000..7d982cb5fbff6 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/integ-user-pool-link-custom-placeholder.template.json @@ -0,0 +1,76 @@ +{ + "Resources": { + "myuserpool01998219": { + "Type": "AWS::Cognito::UserPool", + "Properties": { + "AccountRecoverySetting": { + "RecoveryMechanisms": [ + { + "Name": "verified_phone_number", + "Priority": 1 + }, + { + "Name": "verified_email", + "Priority": 2 + } + ] + }, + "AdminCreateUserConfig": { + "AllowAdminCreateUserOnly": true + }, + "DeletionProtection": "INACTIVE", + "SmsVerificationMessage": "The verification code to your new account is {####}", + "UserPoolName": "MyUserPool", + "VerificationMessageTemplate": { + "DefaultEmailOption": "CONFIRM_WITH_LINK", + "EmailMessageByLink": "You have been invited to join our awesome app! {##Click here to verify your email##}", + "EmailSubjectByLink": "Invite to join our awesome app!", + "SmsMessage": "The verification code to your new account is {####}" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Outputs": { + "userpoolid": { + "Value": { + "Ref": "myuserpool01998219" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/integ.json new file mode 100644 index 0000000000000..837060dde243f --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/integ.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "testCases": { + "user-pool-link-custom-placeholder-integ-test/DefaultTest": { + "stacks": [ + "integ-user-pool-link-custom-placeholder" + ], + "cdkCommandOptions": { + "deploy": { + "args": { + "rollback": true + } + } + }, + "assertionStack": "user-pool-link-custom-placeholder-integ-test/DefaultTest/DeployAssert", + "assertionStackName": "userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/manifest.json new file mode 100644 index 0000000000000..e3a71d145f54d --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/manifest.json @@ -0,0 +1,119 @@ +{ + "version": "36.0.0", + "artifacts": { + "integ-user-pool-link-custom-placeholder.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integ-user-pool-link-custom-placeholder.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integ-user-pool-link-custom-placeholder": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integ-user-pool-link-custom-placeholder.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/8933ce2d15790f339b208cf26b074a443813cb433115af260ab927c97e241c47.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integ-user-pool-link-custom-placeholder.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "integ-user-pool-link-custom-placeholder.assets" + ], + "metadata": { + "/integ-user-pool-link-custom-placeholder/myuserpool/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "myuserpool01998219" + } + ], + "/integ-user-pool-link-custom-placeholder/user-pool-id": [ + { + "type": "aws:cdk:logicalId", + "data": "userpoolid" + } + ], + "/integ-user-pool-link-custom-placeholder/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-user-pool-link-custom-placeholder/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-user-pool-link-custom-placeholder" + }, + "userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314.assets" + ], + "metadata": { + "/user-pool-link-custom-placeholder-integ-test/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/user-pool-link-custom-placeholder-integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "user-pool-link-custom-placeholder-integ-test/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/tree.json new file mode 100644 index 0000000000000..d66f0e544396f --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/tree.json @@ -0,0 +1,156 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "integ-user-pool-link-custom-placeholder": { + "id": "integ-user-pool-link-custom-placeholder", + "path": "integ-user-pool-link-custom-placeholder", + "children": { + "myuserpool": { + "id": "myuserpool", + "path": "integ-user-pool-link-custom-placeholder/myuserpool", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-user-pool-link-custom-placeholder/myuserpool/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Cognito::UserPool", + "aws:cdk:cloudformation:props": { + "accountRecoverySetting": { + "recoveryMechanisms": [ + { + "name": "verified_phone_number", + "priority": 1 + }, + { + "name": "verified_email", + "priority": 2 + } + ] + }, + "adminCreateUserConfig": { + "allowAdminCreateUserOnly": true + }, + "deletionProtection": "INACTIVE", + "smsVerificationMessage": "The verification code to your new account is {####}", + "userPoolName": "MyUserPool", + "verificationMessageTemplate": { + "defaultEmailOption": "CONFIRM_WITH_LINK", + "emailMessageByLink": "You have been invited to join our awesome app! {##Click here to verify your email##}", + "emailSubjectByLink": "Invite to join our awesome app!", + "smsMessage": "The verification code to your new account is {####}" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cognito.CfnUserPool", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cognito.UserPool", + "version": "0.0.0" + } + }, + "user-pool-id": { + "id": "user-pool-id", + "path": "integ-user-pool-link-custom-placeholder/user-pool-id", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnOutput", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integ-user-pool-link-custom-placeholder/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integ-user-pool-link-custom-placeholder/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "user-pool-link-custom-placeholder-integ-test": { + "id": "user-pool-link-custom-placeholder-integ-test", + "path": "user-pool-link-custom-placeholder-integ-test", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "user-pool-link-custom-placeholder-integ-test/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "user-pool-link-custom-placeholder-integ-test/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "user-pool-link-custom-placeholder-integ-test/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "user-pool-link-custom-placeholder-integ-test/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "user-pool-link-custom-placeholder-integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314.assets.json new file mode 100644 index 0000000000000..819351d69a81e --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.js.snapshot/userpoollinkcustomplaceholderintegtestDefaultTestDeployAssert5C199314.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.ts new file mode 100644 index 0000000000000..65e04962e0437 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-link-custom-placeholder.ts @@ -0,0 +1,32 @@ +import { App, CfnOutput, RemovalPolicy, Stack } from 'aws-cdk-lib'; +import { UserPool, VerificationEmailStyle } from 'aws-cdk-lib/aws-cognito'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +const app = new App(); +const stack = new Stack(app, 'integ-user-pool-link-custom-placeholder'); + +const userpool = new UserPool(stack, 'myuserpool', { + userPoolName: 'MyUserPool', + removalPolicy: RemovalPolicy.DESTROY, + deletionProtection: false, + userVerification: { + emailStyle: VerificationEmailStyle.LINK, + emailSubject: 'Invite to join our awesome app!', + emailBody: 'You have been invited to join our awesome app! {##Click here to verify your email##}', + }, +}); + +new CfnOutput(stack, 'user-pool-id', { + value: userpool.userPoolId, +}); + +new IntegTest(app, 'user-pool-link-custom-placeholder-integ-test', { + testCases: [stack], + cdkCommandOptions: { + deploy: { + args: { + rollback: true, + }, + }, + }, +}); diff --git a/packages/aws-cdk-lib/aws-cognito/README.md b/packages/aws-cdk-lib/aws-cognito/README.md index f058bdbd48ebf..ae7b91ecc4f5a 100644 --- a/packages/aws-cdk-lib/aws-cognito/README.md +++ b/packages/aws-cdk-lib/aws-cognito/README.md @@ -123,9 +123,9 @@ Alternatively, users can use link as a verification method. The following code s properties relevant to these verification messages and link verification method. ```ts -new UserPool(stack, 'myuserpool', { +new cognito.UserPool(this, 'myuserpool', { userVerification: { - emailStyle: VerificationEmailStyle.LINK, + emailStyle: cognito.VerificationEmailStyle.LINK, emailSubject: 'Invite to join our awesome app!', emailBody: 'You have been invited to join our awesome app! {##Verify Your Email##}', }, diff --git a/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts b/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts index 0cf936094b946..b0deaf41e8805 100644 --- a/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts +++ b/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts @@ -1047,7 +1047,7 @@ export class UserPool extends UserPoolBase { private verificationMessageConfiguration(props: UserPoolProps): CfnUserPool.VerificationMessageTemplateProperty { const CODE_TEMPLATE = '{####}'; const VERIFY_EMAIL_TEMPLATE = '{##Verify Email##}'; - const VERIFY_EMAIL_REGEX = /{##.*##}/g; + const VERIFY_EMAIL_REGEX = /{##.*?##}/g; const emailStyle = props.userVerification?.emailStyle ?? VerificationEmailStyle.CODE; const emailSubject = props.userVerification?.emailSubject ?? 'Verify your new account'; From c94a45d09f687149795d68f7a0e21469e462e86b Mon Sep 17 00:00:00 2001 From: Gavin Zhang Date: Tue, 23 Jan 2024 14:52:02 -0800 Subject: [PATCH 3/6] remove g flag from regex --- packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts b/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts index b0deaf41e8805..021d60248f27c 100644 --- a/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts +++ b/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts @@ -1047,7 +1047,7 @@ export class UserPool extends UserPoolBase { private verificationMessageConfiguration(props: UserPoolProps): CfnUserPool.VerificationMessageTemplateProperty { const CODE_TEMPLATE = '{####}'; const VERIFY_EMAIL_TEMPLATE = '{##Verify Email##}'; - const VERIFY_EMAIL_REGEX = /{##.*?##}/g; + const VERIFY_EMAIL_REGEX = /{##.*?##}/; const emailStyle = props.userVerification?.emailStyle ?? VerificationEmailStyle.CODE; const emailSubject = props.userVerification?.emailSubject ?? 'Verify your new account'; From 059a4fe68f6aa8e50ad19c0fdad56c4c7bedb6e0 Mon Sep 17 00:00:00 2001 From: Gavin Zhang Date: Tue, 23 Jan 2024 16:36:45 -0800 Subject: [PATCH 4/6] Update regex to be more specific and less likely to cause polynomial regex issue --- .../aws-cdk-lib/aws-cognito/lib/user-pool.ts | 2 +- .../aws-cognito/test/user-pool.test.ts | 44 +++++++++---------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts b/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts index 021d60248f27c..2ed3b06587263 100644 --- a/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts +++ b/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts @@ -1047,7 +1047,7 @@ export class UserPool extends UserPoolBase { private verificationMessageConfiguration(props: UserPoolProps): CfnUserPool.VerificationMessageTemplateProperty { const CODE_TEMPLATE = '{####}'; const VERIFY_EMAIL_TEMPLATE = '{##Verify Email##}'; - const VERIFY_EMAIL_REGEX = /{##.*?##}/; + const VERIFY_EMAIL_REGEX = /\{##[a-zA-Z0-9\s]*##\}/; const emailStyle = props.userVerification?.emailStyle ?? VerificationEmailStyle.CODE; const emailSubject = props.userVerification?.emailSubject ?? 'Verify your new account'; diff --git a/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts b/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts index e238fa22cb133..04f7892e1d61f 100644 --- a/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts +++ b/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts @@ -169,6 +169,27 @@ describe('User Pool', () => { emailBody: 'invalid email body {##Verify Email##}', }, })).not.toThrow(); + + expect(() => new UserPool(stack, 'Pool7', { + userVerification: { + emailStyle: VerificationEmailStyle.LINK, + emailBody: 'invalid email body ##Verify Email##', + }, + })).toThrow(/Verification email body/); + + expect(() => new UserPool(stack, 'Pool8', { + userVerification: { + emailStyle: VerificationEmailStyle.LINK, + emailBody: 'invalid email body {##Verify !! Email##}', + }, + })).toThrow(/Verification email body/); + + expect(() => new UserPool(stack, 'Pool9', { + userVerification: { + emailStyle: VerificationEmailStyle.LINK, + emailBody: 'invalid email body {##Click here to verify##}', + }, + })).not.toThrow(); }); test('validation is skipped for email and sms messages when tokens', () => { @@ -355,29 +376,6 @@ describe('User Pool', () => { }); }); - test("custom email body with non 'Verify Email' placeholder text", () => { - // GIVEN - const stack = new Stack(); - - // WHEN - new UserPool(stack, 'Pool', { - userVerification: { - emailStyle: VerificationEmailStyle.LINK, - emailSubject: 'Please verify your e-mail', - emailBody: '

Hello world {##Custom Text##}

', - }, - }); - - // THEN - Template.fromStack(stack).hasResourceProperties('AWS::Cognito::UserPool', { - VerificationMessageTemplate: { - DefaultEmailOption: 'CONFIRM_WITH_LINK', - EmailMessageByLink: '

Hello world {##Custom Text##}

', - EmailSubjectByLink: 'Please verify your e-mail', - }, - }); - }); - test('custom sender lambda triggers via properties are correctly configured', () => { // GIVEN const stack = new Stack(); From 2573ea73ae2607372bada254c785c3380bb3be67 Mon Sep 17 00:00:00 2001 From: Gavin Zhang Date: Wed, 24 Jan 2024 16:01:39 -0800 Subject: [PATCH 5/6] update description of email body --- packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts b/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts index 04f7892e1d61f..20d6b11b4c9ee 100644 --- a/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts +++ b/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts @@ -159,14 +159,14 @@ describe('User Pool', () => { expect(() => new UserPool(stack, 'Pool5', { userVerification: { emailStyle: VerificationEmailStyle.LINK, - emailBody: 'invalid email body {####}', + emailBody: 'valid email body {####}', }, })).not.toThrow(); expect(() => new UserPool(stack, 'Pool6', { userVerification: { emailStyle: VerificationEmailStyle.LINK, - emailBody: 'invalid email body {##Verify Email##}', + emailBody: 'valid email body {##Verify Email##}', }, })).not.toThrow(); @@ -187,7 +187,7 @@ describe('User Pool', () => { expect(() => new UserPool(stack, 'Pool9', { userVerification: { emailStyle: VerificationEmailStyle.LINK, - emailBody: 'invalid email body {##Click here to verify##}', + emailBody: 'valid email body {##Click here to verify##}', }, })).not.toThrow(); }); From 73267831c0add8ea36255fb31001b25028f24e9d Mon Sep 17 00:00:00 2001 From: Gavin Zhang Date: Wed, 24 Jan 2024 17:10:24 -0800 Subject: [PATCH 6/6] Update regex to allow non alphanumeric characters --- packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts | 6 +++++- packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts b/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts index 2ed3b06587263..8da548198eb32 100644 --- a/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts +++ b/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts @@ -1047,7 +1047,11 @@ export class UserPool extends UserPoolBase { private verificationMessageConfiguration(props: UserPoolProps): CfnUserPool.VerificationMessageTemplateProperty { const CODE_TEMPLATE = '{####}'; const VERIFY_EMAIL_TEMPLATE = '{##Verify Email##}'; - const VERIFY_EMAIL_REGEX = /\{##[a-zA-Z0-9\s]*##\}/; + /** + * Email message placeholder regex + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-userpool-verificationmessagetemplate.html#cfn-cognito-userpool-verificationmessagetemplate-emailmessagebylink + */ + const VERIFY_EMAIL_REGEX = /\{##[\p{L}\p{M}\p{S}\p{N}\p{P}\s*]*##\}/u; const emailStyle = props.userVerification?.emailStyle ?? VerificationEmailStyle.CODE; const emailSubject = props.userVerification?.emailSubject ?? 'Verify your new account'; diff --git a/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts b/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts index 20d6b11b4c9ee..b80c669e205d7 100644 --- a/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts +++ b/packages/aws-cdk-lib/aws-cognito/test/user-pool.test.ts @@ -180,9 +180,9 @@ describe('User Pool', () => { expect(() => new UserPool(stack, 'Pool8', { userVerification: { emailStyle: VerificationEmailStyle.LINK, - emailBody: 'invalid email body {##Verify !! Email##}', + emailBody: 'valid email body {##Verify !! Email##}', }, - })).toThrow(/Verification email body/); + })).not.toThrow(); expect(() => new UserPool(stack, 'Pool9', { userVerification: {