Skip to content

Commit

Permalink
fix(cognito): user pool - link style email verification fails to depl…
Browse files Browse the repository at this point in the history
…oy (#6938)

A couple of changes -

- `emailVerificationMessage` and `emailVerificationSubject`
properties are not set when link-style verification is used.
This is root cause for the error message reported by the user.

- The defaults `emailBody` and `emailSubject` are modified when
link-style verification is used. They will now correctly contain the
template placeholder `{##Verify Email##}`.

- Validations are now added that email and SMS verification
messages have the mandatory placeholders.

fixes #6811
  • Loading branch information
Niranjan Jayakar authored Mar 24, 2020
1 parent 1edd507 commit b5c60d5
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 24 deletions.
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

0 comments on commit b5c60d5

Please sign in to comment.