Send custom attributes to Amazon Cognito and inject them into tokens
Carlos Alessandro Ribeiro and Mauricio Mendoza, Amazon Web Services
Summary
Sending custom attributes to an Amazon Cognito authentication process can provide additional context to an application, enable more granular access controls, and make it easier to manage user profiles and authentication requirements. These features are useful in a wide range of applications and scenarios, and they can help you improve the overall security and functionality of an application.
This pattern shows how to send custom attributes to an Amazon Cognito authentication process when an application needs to provide additional context to the access token or identity (ID) token. You use the Node.js as the backend application. The application authenticates a user from an Amazon Cognito user pool and passes custom attributes that are needed for token generation. You can use AWS Lambda triggers for Amazon Cognito to customize your authentication process without major code customization or significant effort.
Important
The code and samples in this pattern are not recommended for production workloads because they are intended for demonstration purposes only. For production workloads, additional configuration is required on the client side. Use this pattern as a reference for pilot or proof-of-concept purposes only.
Prerequisites and limitations
Prerequisites
An active AWS account
Permissions to create and manage Amazon Cognito user pools and AWS Lambda functions
AWS Command Line Interface (AWS CLI), installed and configured
An integrated development environment (IDE) that supports Node.js
Node.js version 18 or later, installed
npm version 8 or later, installed
TypeScript, installed
Limitations
This pattern is not applicable for application integration through the Client Credentials authentication flow.
The pre-token generation trigger can add or change only some attributes of the access token and identity token. For more information, see Pre token generation Lambda trigger in the Amazon Cognito documentation.
Architecture
Target architecture
The following diagram shows the target architecture for this pattern. It also shows how the Node.js application might work with a backend to update databases. However, the backend database updates are outside the scope of this pattern.

The diagram shows the following workflow:
The Node.js application issues an access token with custom attributes to the Amazon Cognito user pool.
The Amazon Cognito user pool initiates the pre-token generation Lambda function, which customizes the access and ID tokens.
The Node.js application makes an API call through Amazon API Gateway.
Note
The other architectural components shown in this architecture are for example only and are out of scope for this pattern.
Automation and scale
You can automate the provisioning of Amazon Cognito user pools, AWS Lambda functions, database instances, and other resources by using AWS CloudFormation, the AWS Cloud Development Kit (AWS CDK), HashiCorp Terraform
Tools
AWS services
Amazon API Gateway helps you create, publish, maintain, monitor, and secure REST, HTTP, and WebSocket APIs at any scale.
Amazon Cognito provides authentication, authorization, and user management for web and mobile apps.
Amazon Elastic Container Service (Amazon ECS) is a fast and scalable container management service that helps you run, stop, and manage containers on a cluster.
AWS Lambda is a compute service that helps you run code without needing to provision or manage servers. It runs your code only when needed and scales automatically, so you pay only for the compute time that you use.
AWS SDK for JavaScript provides a JavaScript API for AWS services. You can use it to build libraries or applications for Node.js or the browser.
Other tools
Best practices
We recommend that you implement the following best practices:
Secrets and sensitive data – Do not store secrets or sensitive data within the application. Use an external system that the application can pull the data from, such as AWS AppConfig, AWS Secrets Manager, or AWS Systems Manager Parameter Store.
Standardized deployment – Use CI/CD pipelines to deploy your applications. You can use services such as AWS CodeBuild and AWS CodePipeline.
Token expiration – Set a short expiration date for the access token.
Use a secure connection – All communication between the client application and the backend should be encrypted by using SSL/TLS. Use AWS Certificate Manager (ACM) to generate and manage SSL/TLS certificates, and use Amazon CloudFront or Elastic Load Balancing to handle SSL/TLS termination.
Validate user input – Make sure that all user input is validated to prevent injection attacks and other security vulnerabilities. Use input validation libraries and services such as Amazon API Gateway and AWS WAF to prevent common attack vectors.
Use IAM roles – Use AWS Identity and Access Management (IAM) roles to control access to AWS resources and make sure that only authorized users have access. Follow the principle of least privilege and make sure that each user has only the necessary permissions to perform their role.
Use a password policy – Configure a password policy that meets your security requirements, such as minimum length, complexity, and expiration. Use Secrets Manager or AWS Systems Manager Parameter Store to store and manage passwords securely.
Enable multi-factor authentication (MFA) – Enable MFA for all users to provide an additional layer of security and reduce the risk of unauthorized access. Use AWS IAM Identity Center or Amazon Cognito to enable MFA and other authentication methods.
Store sensitive information securely – Store sensitive information, such as passwords and access tokens, securely by using AWS Key Management Service (AWS KMS) or other encryption services.
Use strong authentication methods – To increase the security of the authentication process, use strong authentication methods, such as biometric authentication or multi-factor authentication.
Monitor for suspicious activity – Use AWS CloudTrail and other monitoring tools to monitor for suspicious activity and potential security threats. Set up automated alerts for unusual activity, and use Amazon GuardDuty or AWS Security Hub to detect potential threats.
Regularly review and update security policies – Regularly review and update your security policies and procedures to make sure that they meet your changing security requirements and best practices. Use AWS Config to track and audit changes to your security policies and procedures.
Automated sign-up – Do not enable automated sign-up to an Amazon Cognito user pool. For more information, see Reduce risks of user sign-up fraud and SMS pumping with Amazon Cognito user pools
(AWS blog post).
For additional best practices, see Security best practices for Amazon Cognito user pools in the Amazon Cognito documentation.
Epics
Task | Description | Skills required |
---|---|---|
Create a user pool. |
For more information and instructions for how to set up a user pool in the AWS Management Console, see Getting started with user pools and Add more features and security options to your user pool. TipTo reduce costs, use the Essentials plan or Lite plan to test this pattern. For more information, see Amazon Cognito pricing | App developer, AWS DevOps |
Add a user to the user pool. | Enter the following command to create one user in the Amazon Cognito user pool:
| App developer, AWS DevOps |
Add the app client to the user pool. |
| AWS systems administrator, AWS administrator, AWS DevOps, App developer |
Create a Lambda trigger for pre-token generation. |
| AWS DevOps, App developer |
Customize the user pool workflow. |
For more information, see Customizing user pool workflows with Lambda triggers in the Amazon Cognito documentation. | AWS DevOps, App developer |
Task | Description | Skills required |
---|---|---|
Create the Node.js application. |
| App developer |
Implement the authentication logic. |
NoteYou can create your own TypeScript file or modify the sample provided as needed for your use case. | App developer |
Configure the environment variables and configuration file. | In a terminal, enter the following commands to create the environment variables:
ImportantDo not hardcode secrets or expose your credentials. | App developer |
Run the application. | Enter the following commands to run the application and confirm that it is working:
| App developer |
Confirm that the custom attributes are injected into the tokens. | Use the debugging features for your IDE to view the access and ID tokens. Confirm that the custom attributes were added. For sample tokens, see the Additional information section of this pattern. | App developer |
Troubleshooting
Issue | Solution |
---|---|
Invalid client ID when trying to authenticate the user | This error typically occurs when you are using a client ID with a generated client secret. You must create a client ID without a secret attached to it. For more information, see Application-specific settings with app clients. |
Related resources
Customizing user pool workflows with Lambda triggers (Amazon Cognito documentation)
Pre token generation Lambda trigger (Amazon Cognito documentation)
CognitoIdentityProviderClient (AWS SDK for JavaScript documentation)
cognito-idp
(AWS CLI documentation)
Additional information
Sample TypeScript file
The following code sample is a TypeScript file that invokes the authentication process by using an AWS SDK to send custom attributes to Amazon Cognito:
import * as AmazonCognitoIdentity from "amazon-cognito-identity-js"; const userPoolId: string = process.env.USER_POOL_ID ?? ''; const clientId: string = process.env.CLIENT_ID ?? ''; const poolData = { UserPoolId: userPoolId, ClientId: clientId }; const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData); export const loginWithCognitoSDK = function (userName: string, password: string) { const authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails({ Username: userName, Password: password, ClientMetadata: { customGroup: "MyCustomGroup", customApplicationData: "Custom data from a custom application" } }); const userData = { Username: userName, Pool: userPool }; const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData); // Authenticate the user using the authenticationDetails object cognitoUser.authenticateUser(authenticationDetails, { onSuccess: function (result: any) {}, onFailure: function (err: any) {}, }); } loginWithCognitoSDK(process.env.USERNAME ?? '', process.env.PASSWORD ?? '');
The sample uses the AuthenticationDetails
model from the SDK for JavaScript to provide the username, password, and the ClientMetadada
. After the authentication in Amazon Cognito, the client metadata can be retrieved from the access and ID tokens.
Sample Lambda function
The following code sample is a Lambda function that is linked to the Pre-Generation Token from Amazon Cognito. It helps you customize the access token and ID token that Amazon Cognito uses. The tokens are passed through the integrations between your architecture. This sample includes a custom claim attribute called customApplicationData
and a custom group name called MyCustomGroup
:
export const handler = async(event, context, callback) => { event.response = { claimsOverrideDetails: { claimsToAddOrOverride: { customApplicationData: event.request.clientMetadata.customApplicationData }, groupOverrideDetails: { groupsToOverride: [event.request.clientMetadata.customGroup] } } }; callback(null, event); };
Sample access token
You can decode the access token to visualize the custom attributes that were added. The following is a sample access token:
{ "sub": "6daf331f-4451-48b4-abde-774579299204", "cognito:groups": [ "MyCustomGroup" ], "iss": "https://cognito-idp.<REGION>.amazonaws.com/<USERPOOL_ID>", "client_id": "<YOUR_CLIENT_ID>", "origin_jti": "acff7e91-09f9-4fde-8eec-38b0f8c47cdc", "event_id": "c5113a9c-1f01-435b-9b73-a5cd3e88514e", "token_use": "access", "scope": "aws.cognito.signin.user.admin", "auth_time": 1677979246, "exp": 1677982846, "iat": 1677979246, "jti": "5c9c2708-a871-4428-bd9b-18ad261bea90", "username": "<USER_NAME>" }
Sample ID token
You can decode the access token to visualize the custom attributes that were added. The following is a sample access token:
{ "sub": "6daf331f-4451-48b4-abde-774579299204", "cognito:groups": [ "MyCustomGroup" ], "iss": "https://cognito-idp.<REGION>.amazonaws.com/<USERPOOL_ID>", "cognito:username": "<USER_NAME>", "origin_jti": "acff7e91-09f9-4fde-8eec-38b0f8c47cdc", "customApplicationData": "Custom data from a custom application", "aud": "<YOUR_CLIENT_ID>", "event_id": "c5113a9c-1f01-435b-9b73-a5cd3e88514e", "token_use": "id", "auth_time": 1677979246, "exp": 1677982846, "iat": 1677979246, "jti": "f7ca006b-f25b-44d2-a7a4-6e6423f4201f", "email": "<USER_EMAIL>" }