AWS Cognito: Send email by using a third-party provider

Kranti Kulkarni
Globant
Published in
6 min readAug 17, 2023

AWS Cognito: Custom Email Sender Lambda Trigger

Photo by Cytonn Photography on Unsplash

AWS Cognito is a user authorization and authentication service offered by AWS. It provides you with many options for sign-up and sign-in functionalities. You can customize your authentication process using AWS lambda functions as triggers in Cognito. One of the important triggers AWS Cognito provides is the Custom email sender Lambda trigger. Amazon Cognito invokes the custom email sender trigger as part of the sign-up, forgot password, update or verify user attribute, create a user via admin API, etc. It is mainly used where you want a third-party provider or custom methods to send email notifications to your users from your AWS Lambda function code. So, get ready to dive deep into setting up your custom email sender trigger.

Let’s get started

Below architecture diagram shows the functioning of the ‘Custom Email Sender Lambda Trigger’ and trigger configuration. The main components of the architecture are the Cognito user pool, KMS Key, and the lambda function. Whenever a user sends a request, the Cognito user pool sends an encrypted verification code with the help of the KMS key to the lambda function. The lambda function implements logic to send an email via a third-party provider or custom method.

The Architecture diagram

KMS key configuration

To begin with, create a symmetric encryption key in AWS Key Management Service (KMS). When Cognito generates authentication codes for multifactor authentication (MFA) or temporary passwords, it uses this key to encrypt them. These are passed to the lambda function. While sending the code to the user, you can decrypt them using AWS SDK.

Symmetric encryption key in KMS

Cognito service should be able to access our KMS key. From the AWS console, in the key policy, grant Amazon Cognito service principal cognito-idp.amazonaws.com access to encrypt codes with the KMS key.

Key policy for encryption key

Lambda function as a trigger

Now create a lambda function you want to assign as a custom trigger. It handles sending authentication codes in emails via third-party providers or custom methods.

As mentioned above, Cognito generates an encrypted authentication code using the KMS key created. In the use case where you want to send the authentication code to the user by email, you need to decrypt it by using AWS SDK before sending. Below JavaScript code snippet can be used as a reference for this :

const AWS = require('aws-sdk');
const b64 = require('base64-js');
const encryptionSdk = require('@aws-crypto/client-node');
const { encrypt, decrypt } = encryptionSdk.buildClient(encryptionSdk.CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT);

const generatorKeyId = process.env.KEY_ALIAS; // environment variable for alias of the key
const keyIds = [ process.env.KEY_ARN ]; // ARN of the key
const keyring = new encryptionSdk.KmsKeyringNode({ generatorKeyId, keyIds })

//Decrypt the secret code using encryption SDK.
let verificationCode;
if(event.request.code){
const { plaintext, messageHeader } = await decrypt(keyring, b64.toByteArray(event.request.code));
verificationCode = plaintext
}

Grant Amazon Cognito service access to invoke the Lambda function. You can use the below command for this:

aws lambda add-permission --function-name {lambda_arn} --statement-id "invokePermissions" --action lambda:InvokeFunction --principal cognito-idp.amazonaws.com

In the AWS console of the lambda function, you can verify the above permission in the Permissions section of the configuration tab.

A resource-based policy of the lambda function

This trigger can be used in various functionalities like the sign-up process, forgot password flow, update or verify attributes, etc. The source of the trigger is passed in the parameter ‘triggerSource’ in the input event of the lambda function. Below code snippet shows an example of handling the source of the event.

if(event.triggerSource == 'CustomEmailSender_SignUp'){
//Send an email message to your user via a custom provider.
//Include the temporary password in the message.
}
else if(event.triggerSource == 'CustomEmailSender_ResendCode'){
//A user requests a replacement code to reset their password.
}
else if(event.triggerSource == 'CustomEmailSender_ForgotPassword'){
//A user requests a code to reset their password.
}
else if(event.triggerSource == 'CustomEmailSender_UpdateUserAttribute'){
//A user updates an email address or phone number attribute and
//Amazon Cognito sends a code to verify the attribute.
}
else if(event.triggerSource == 'CustomEmailSender_VerifyUserAttribute'){
//A user creates a new email address or phone number attribute and
//Amazon Cognito sends a code to verify the attribute.
}
else if(event.triggerSource == 'CustomEmailSender_AdminCreateUser'){
//You create a new user in your user pool and
//Amazon Cognito sends them a temporary password.
}
else if(event.triggerSource == 'CustomEmailSender_AccountTakeOverNotification'){
//Amazon Cognito detects an attempt to take over a user account and
//sends the user a notification.
}

We can use this trigger in the use case of the sign-up journey, where you’re fetching email Ids from a different source and don’t want to save them in the Cognito user pool. You must set some dummy email IDs in the user pool in the signup process. In the lambda function, fetch the actual email Id and send an email via a custom method. Below architecture diagram shows us the configuration of this scenario.

The Architecture diagram

Trigger configuration

The next step is to add a custom sender Lambda trigger to your user pool. Please note: currently, you can’t assign a custom email sender trigger in the Amazon Cognito console. You can set a trigger with the LambdaConfig parameter in a CreateUserPool or UpdateUserPool API request. Or you can also use the below command for it:

aws cognito-idp update-user-pool --user-pool-id {user_pool_id} --lambda-config "CustomEmailSender={LambdaVersion=V1_0,LambdaArn={lambda-arn} },KMSKeyID={key-arn}"

To verify if your triggers are configured correctly, refer to the user pool properties tab of your Cognito user pool in the AWS console.

Cognito user pool properties

Recommended settings

We’re almost there. You may encounter some difficulties where the trigger does not work even if all the above steps are correct. Below are some tips related to the Cognito use pool settings in the AWS console:

If you’re using this trigger for the sign-up process, it is recommended to enable the ‘Allow Cognito to automatically send messages to verify and confirm’ flag of the user pool. The flag is in the ‘Attribute verification and user account confirmation’ section of the ‘Sign-up experience’ tab.

Recommended settings 1

Before running the command to set the trigger, you should disable the ‘Verifying attribute changes’ flag. If the flag is enabled, you may get theInvalidParameterException.To fix this issue, you can include its parameter — auto-verified-attributes=”<attribute_name>” in the command.

Recommended settings 2

Summary

AWS Cognito provides you with the Custom Email Sender Lambda trigger to customize the authentication journey of the user. Using this functionality, you can use a third-party provider or custom methods to send an email from the lambda function. In the trigger configuration, the KMS encryption key is provided along with the lambda function. AWS uses the key to encrypt the authorization code or temporary passwords. They can be decrypted using AWS SDK and passed to the users. You can identify the source of the trigger from the parameter passed to the lambda function and use it in the code.

References:

Further Reading:

Custom SMS Sender Lambda trigger

--

--