3

When sending a custom email message for CustomMessage_AdminCreateUser trigger, I successfully change the emailSubject attribute in the event received from Amazon Cognito, but can't seem to change the emailMessage attribute.

The email sent from Cognito contains the correct customized subject, but the message is not customized at all, and it's always the one that is set in the Cognito pool settings.

The lambda handler which handles the event received from Cognito successfully customizes the message for these triggers:

  • CustomMessage_SignUp
  • CustomMessage_ResendCode
  • CustomMessage_ForgotPassword

But I can't seem to get it working (at least not completely) for CustomMessage_AdminCreateUser trigger.

I tried setting the email_verified user attribute to true to see if that attribute depends on successfully sending the customized mail to the created user. Also, I tried running the lambda in Docker container to see the output of the final event returned to the Cognito, but the event contains the correct data, both email subject and email message were customized.

def lambda_handler(event, context):
    if event['userPoolId'] == os.getenv('cognitoPoolId'):
        if CustomMessageTriggerEnum.has_value(event.get('triggerSource')):
            custom_message_trigger = CustomMessageTriggerEnum(event.get('triggerSource'))

            if custom_message_trigger == CustomMessageTriggerEnum.CustomMessageAdminCreateUser:
                custom_message_trigger = CustomMessageAdminCreateUser(event)
            else:
                return None

            custom_response = custom_message_trigger.get_custom_response(
                custom_message_trigger.ACTION,
                custom_message_trigger.EMAIL_SUBJECT,
                custom_message_trigger.EMAIL_MESSAGE
            )
            event = custom_message_trigger.set_custom_response(**custom_response)

            return event
class CustomMessageAdminCreateUser(BaseCustomMessageTrigger):
    """ Custom message admin create user trigger """

    ACTION = "changepassword"
    EMAIL_SUBJECT = "Welcome to {service}"
    EMAIL_MESSAGE = "Your account has been created. <a href='{0}'>Click here</a> to set your password and start using {service}."

    def __init__(self, event):
        super().__init__(event)
class BaseCustomMessageTrigger():
    """ Base custom message trigger """
    def __init__(self, event):
        self.event = event

    def get_custom_response(self, action, email_subject, email_message):
        """ Gets custom response params as dictionary """
        request = self.event.get('request')
        custom_response = {}
        url = self.get_url(
            action=action,
            code=request.get('codeParameter'),
            email=urlencode({'email': request['userAttributes'].get('email')})
        )
        custom_response['emailSubject'] = email_subject
        custom_response['emailMessage'] = email_message.format(url)

        return custom_response

    def set_custom_response(self, **kwargs):
        """ Updates the event response with provided kwargs """
        response = self.event.get('response')
        response.update(**kwargs)

        return self.event

    def get_url(self, action, code, email):
        """ Used for constructing URLs. """
        rawUrl = 'https://{0}/{1}?code={2}&{3}'
        return rawUrl.format(domain, action, code, email)
jkeys
  • 3,803
  • 11
  • 39
  • 63
Josip Kolarić
  • 456
  • 1
  • 7
  • 21

5 Answers5

9

Just to clarify the email should literally contain the values in event.request.usernameParameter and event.request.codeParameter.

I found Cognito ignored my email body because the token {username} was missing even though {####} was there.

dvanrensburg
  • 1,351
  • 1
  • 14
  • 21
  • How did you found it out, is there some logging for cognito? – Townsheriff May 22 '21 at 12:04
  • 1
    @Townsheriff Sorry I don't know. I can't remember. I think I found it through trial and error. I got a clue about what was wrong from Kristine's answer and tried different permutations until it dawned on me that it wants to see those values in the email body. – dvanrensburg May 24 '21 at 03:34
5

You have to avoid urlencode on response parameters because Lambda will add placeholders which will be replaced by Cognito later.

  • 1
    Thanks, this solves the current issue, but the next issue that comes up is how to encode the special characters in the `username` field if Cognito does not handle that by itself. Special characters like `+` will be considered as a blank space, and those kind of emails are not valid. – Josip Kolarić Aug 07 '19 at 10:34
4

I was having the exact same issue.

My initial custom message:

<html>
    <body>
      <p> Welcome. Follow the link below to unlock your account and set a new password.</p>
      <a href="http://localhost:3000/setNewPasswordcode_parameter=${event.request.codeParameter}&user_name=${event.userName}">Set Password</a>
    </body>
</html>

The problem is that you need to use both: event.request.codeParameter and event.request.usernameParameter (so in my initial code case not event.userName) in the template.

Besides that the codeParameter and usernameParameter have to contain only URI valid characters. So if you username is an email address containing '@' symbol, you cannot use it in the link.

Workaround: For the temporary password you can make sure only URI valid characters are used when calling adminCreateUser() by passing in a TemporaryPassword parameter.


cognitoIdentityServiceProvider.adminCreateUser({ 
  UserPoolId,
  Username, 
  TemporaryPassword
}).promise()

For the link you can use event.userName - this should be the sub of the cognito user. Just make sure to include event.request.usernameParametersomewhere else in the email. In my case I start the mail with Welcome, ${event.request.usernameParameter}!

1

One important thing for CustomMessage_AdminCreateUser trigger is that you have to include username and password in mail emailMessage.

def get_url(self, action, code, email):
        """ Used for constructing URLs. """
        rawUrl = 'https://{0}/{1}?code={2}&{3}'
        return rawUrl.format(domain, action, code, email)

Please pass domain at get_url method.

Saiful Azad
  • 1,823
  • 3
  • 17
  • 27
  • Both `username` and `password` are included in the URL as query params, I can access those fields from the Cognito event passed into the lambda handler and use them to format the email message the way I want. Are you suggesting that I must explicitly include them in the message, even though I just want the user to be able to click the link instead of copying the username and temporary password for completing registration? – Josip Kolarić Jul 05 '19 at 11:26
  • Please pass domain at get_url method. Signature get_url(self, action, code, email): has no `domain` as arg. Can u plz share the cloudwatch log? – Saiful Azad Jul 06 '19 at 01:58
  • There is no need to pass the `domain` since it's a public variable set trough environment variable. Cloudwatch doesn't have any logs for that, I added custom logging to see the final `emailSubject` and `emailMessage` fields in event, and both of those fields are exactly as they should be, but received message is different. Also the email subject is sent correctly, but the actual email message is not. – Josip Kolarić Jul 06 '19 at 11:07
  • Exactly, the mail is sent, email subject is the one expected, but the email message is not. – Josip Kolarić Jul 07 '19 at 01:52
  • It's working for me now. I had the same issue where subject was correct but not the message. Cloudwatch didnt show any errors. As Saiful mentioned in his answer, I had to make sure both fields were included in the emailMessage: ${event.request.usernameParameter} ${event.request.codeParameter} – ken604 Nov 18 '22 at 22:29
0

In my particular scenario, the issue was due to an overly lengthy HTML template. The character limit for such templates is 20000, however, mine was approximately 27000 characters due to the inclusion of an image in base64 format.

The best approach if your template isn't working: manually paste the HTML message into the corresponding message template via the Cognito User Interface.

User pool -> Messaging -> Message templates -> Select one and edit -> Paste your template in Email message and click Save changes.

enter image description here

mch.zawalski
  • 558
  • 4
  • 5