From 1dbfa14b650e08a0b91ab2c367e37e54d81298c1 Mon Sep 17 00:00:00 2001 From: GZ Date: Wed, 24 Jan 2024 18:38:44 -0800 Subject: [PATCH] fix(cognito): allow custom email msg placeholder (#28832) When I change the text in the "{##Verify Email##}" placeholder, e.g. to "{##verify your email##}", cdk synth and cdk deploy commands yield the error, ``` Error: Verification email body must contain the template string '{##Verify Email##}' ``` This is incorrect as that documentation states that this string may be customised. https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-settings-email-verification-message-customization.html Help text in the AWS Cognito console reads: You can customize this message with HTML. "Verify email" is the text that will be displayed over the clickable link in the message. You can customize the "Verify email" string, but the variable - some text enclosed by "{##" and "##}" - must be kept in the message. Expected Behavior Be able to change the emailBody property of a UserPool to include the placeholder of format, "{##Verify Your Email##}" with any custom string allowed by AWS Cognito. Closes https://github.com/aws/aws-cdk/issues/23828 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../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 | 19 +++ .../aws-cdk-lib/aws-cognito/lib/user-pool.ts | 7 +- .../aws-cognito/test/user-pool.test.ts | 27 ++- 12 files changed, 526 insertions(+), 4 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 8616cb0b929ab..ae7b91ecc4f5a 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 cognito.UserPool(this, 'myuserpool', { + userVerification: { + emailStyle: cognito.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..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,6 +1047,11 @@ export class UserPool extends UserPoolBase { private verificationMessageConfiguration(props: UserPoolProps): CfnUserPool.VerificationMessageTemplateProperty { const CODE_TEMPLATE = '{####}'; const VERIFY_EMAIL_TEMPLATE = '{##Verify Email##}'; + /** + * 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'; @@ -1069,7 +1074,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..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 @@ -159,14 +159,35 @@ describe('User Pool', () => { expect(() => new UserPool(stack, 'Pool5', { userVerification: { emailStyle: VerificationEmailStyle.LINK, - emailBody: 'invalid email body {####}', + emailBody: 'valid email body {####}', }, - })).toThrow(/Verification 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(); + + 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: 'valid email body {##Verify !! Email##}', + }, + })).not.toThrow(); + + expect(() => new UserPool(stack, 'Pool9', { + userVerification: { + emailStyle: VerificationEmailStyle.LINK, + emailBody: 'valid email body {##Click here to verify##}', }, })).not.toThrow(); });