Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(cognito): user pool - link style email verification fails to deploy #6938

Merged
merged 3 commits into from
Mar 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
76 changes: 56 additions & 20 deletions packages/@aws-cdk/aws-cognito/lib/user-pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export interface SignInAliases {
export interface AutoVerifiedAttrs {
/**
* Whether the email address of the user should be auto verified at sign up.
*
* Note: If both `email` and `phone` is set, Cognito only verifies the phone number. To also verify email, see here -
* https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-email-phone-verification.html
*
* @default - true, if email is turned on for `signIn`. false, otherwise.
*/
readonly email?: boolean;
Expand Down Expand Up @@ -144,7 +148,9 @@ export interface UserVerificationConfig {
* The email body template for the verification email sent to the user upon sign up.
* See https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-settings-message-templates.html to
* learn more about message templates.
* @default 'Hello {username}, Your verification code is {####}'
*
* @default - 'Hello {username}, Your verification code is {####}' if VerificationEmailStyle.CODE is chosen,
* 'Hello {username}, Verify your account by clicking on {##Verify Email##}' if VerificationEmailStyle.LINK is chosen.
*/
readonly emailBody?: string;

Expand All @@ -159,7 +165,9 @@ export interface UserVerificationConfig {
* The message template for the verification SMS sent to the user upon sign up.
* See https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-settings-message-templates.html to
* learn more about message templates.
* @default 'The verification code to your new account is {####}'
*
* @default - 'The verification code to your new account is {####}' if VerificationEmailStyle.CODE is chosen,
* not configured if VerificationEmailStyle.LINK is chosen
*/
readonly smsMessage?: string;
}
Expand Down Expand Up @@ -486,24 +494,14 @@ export class UserPool extends Resource implements IUserPool {
}
}

const emailVerificationSubject = props.userVerification?.emailSubject ?? 'Verify your new account';
const emailVerificationMessage = props.userVerification?.emailBody ?? 'Hello {username}, Your verification code is {####}';
const smsVerificationMessage = props.userVerification?.smsMessage ?? 'The verification code to your new account is {####}';

const defaultEmailOption = props.userVerification?.emailStyle ?? VerificationEmailStyle.CODE;
const verificationMessageTemplate: CfnUserPool.VerificationMessageTemplateProperty =
(defaultEmailOption === VerificationEmailStyle.CODE) ? {
defaultEmailOption,
emailMessage: emailVerificationMessage,
emailSubject: emailVerificationSubject,
smsMessage: smsVerificationMessage,
} : {
defaultEmailOption,
emailMessageByLink: emailVerificationMessage,
emailSubjectByLink: emailVerificationSubject,
smsMessage: smsVerificationMessage
};

const verificationMessageTemplate = this.verificationMessageConfiguration(props);
let emailVerificationMessage;
let emailVerificationSubject;
if (verificationMessageTemplate.defaultEmailOption === VerificationEmailStyle.CODE) {
emailVerificationMessage = verificationMessageTemplate.emailMessage;
emailVerificationSubject = verificationMessageTemplate.emailSubject;
}
const smsVerificationMessage = verificationMessageTemplate.smsMessage;
const inviteMessageTemplate: CfnUserPool.InviteMessageTemplateProperty = {
emailMessage: props.userInvitation?.emailBody,
emailSubject: props.userInvitation?.emailSubject,
Expand Down Expand Up @@ -664,6 +662,44 @@ export class UserPool extends Resource implements IUserPool {
});
}

private verificationMessageConfiguration(props: UserPoolProps): CfnUserPool.VerificationMessageTemplateProperty {
const USERNAME_TEMPLATE = '{username}';
const CODE_TEMPLATE = '{####}';
const VERIFY_EMAIL_TEMPLATE = '{##Verify Email##}';

const emailStyle = props.userVerification?.emailStyle ?? VerificationEmailStyle.CODE;
const emailSubject = props.userVerification?.emailSubject ?? 'Verify your new account';
const smsMessage = props.userVerification?.smsMessage ?? `The verification code to your new account is ${CODE_TEMPLATE}`;

if (emailStyle === VerificationEmailStyle.CODE) {
const emailMessage = props.userVerification?.emailBody ?? `Hello ${USERNAME_TEMPLATE}, Your verification code is ${CODE_TEMPLATE}`;
if (emailMessage.indexOf(CODE_TEMPLATE) < 0) {
throw new Error(`Verification email body must contain the template string '${CODE_TEMPLATE}'`);
}
if (smsMessage.indexOf(CODE_TEMPLATE) < 0) {
throw new Error(`SMS message must contain the template string '${CODE_TEMPLATE}'`);
}
return {
defaultEmailOption: VerificationEmailStyle.CODE,
emailMessage,
emailSubject,
smsMessage,
};
} else {
const emailMessage = props.userVerification?.emailBody ??
`Hello ${USERNAME_TEMPLATE}, Verify your account by clicking on ${VERIFY_EMAIL_TEMPLATE}`;
if (emailMessage.indexOf(VERIFY_EMAIL_TEMPLATE) < 0) {
throw new Error(`Verification email body must contain the template string '${VERIFY_EMAIL_TEMPLATE}'`);
}
return {
defaultEmailOption: VerificationEmailStyle.LINK,
emailMessageByLink: emailMessage,
emailSubjectByLink: emailSubject,
smsMessage,
};
}
}

private signInConfiguration(props: UserPoolProps) {
let aliasAttrs: string[] | undefined;
let usernameAttrs: string[] | undefined;
Expand Down
56 changes: 52 additions & 4 deletions packages/@aws-cdk/aws-cognito/test/user-pool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,22 +96,70 @@ describe('User Pool', () => {
// WHEN
new UserPool(stack, 'Pool', {
userVerification: {
emailStyle: VerificationEmailStyle.LINK
emailStyle: VerificationEmailStyle.LINK,
}
});

// THEN
expect(stack).toHaveResourceLike('AWS::Cognito::UserPool', {
EmailVerificationMessage: 'Hello {username}, Your verification code is {####}',
EmailVerificationSubject: 'Verify your new account',
EmailVerificationMessage: ABSENT,
EmailVerificationSubject: ABSENT,
SmsVerificationMessage: 'The verification code to your new account is {####}',
VerificationMessageTemplate: {
DefaultEmailOption: 'CONFIRM_WITH_LINK',
EmailMessageByLink: 'Hello {username}, Your verification code is {####}',
EmailMessageByLink: 'Hello {username}, Verify your account by clicking on {##Verify Email##}',
EmailSubjectByLink: 'Verify your new account',
SmsMessage: 'The verification code to your new account is {####}',
}
});
}),

test('email and sms verification messages are validated', () => {
const stack = new Stack();

expect(() => new UserPool(stack, 'Pool1', {
userVerification: {
emailStyle: VerificationEmailStyle.CODE,
emailBody: 'invalid email body',
}
})).toThrow(/Verification email body/);

expect(() => new UserPool(stack, 'Pool2', {
userVerification: {
emailStyle: VerificationEmailStyle.CODE,
emailBody: 'valid email body {####}',
}
})).not.toThrow();

expect(() => new UserPool(stack, 'Pool3', {
userVerification: {
emailStyle: VerificationEmailStyle.CODE,
smsMessage: 'invalid sms message',
}
})).toThrow(/SMS message/);

expect(() => new UserPool(stack, 'Pool4', {
userVerification: {
emailStyle: VerificationEmailStyle.CODE,
smsMessage: 'invalid sms message {####}',
}
})).not.toThrow();

expect(() => new UserPool(stack, 'Pool5', {
userVerification: {
emailStyle: VerificationEmailStyle.LINK,
emailBody: 'invalid email body {####}',
}
})).toThrow(/Verification email body/);

expect(() => new UserPool(stack, 'Pool6', {
userVerification: {
emailStyle: VerificationEmailStyle.LINK,
emailBody: 'invalid email body {##Verify Email##}',
}
})).not.toThrow();
});

test('user invitation messages are configured correctly', () => {
// GIVEN
const stack = new Stack();
Expand Down