How do I change the attributes of an Amazon Cognito user pool after creation?

Last updated: 2019-06-24

I created an Amazon Cognito user pool and now I want to change the standard attributes required for user registration. How do I change the attributes?

Short Description

You can't change standard user pool attributes after a user pool is created. Instead, create a new user pool with the attributes that you want to require for user registration. Then, migrate existing users to the new user pool by using an AWS Lambda function as a user migration trigger.

Note: You can add custom attributes to an existing user pool, but these attributes aren't required for user registration.

Resolution

Set up a new user pool with different attributes

Create a new user pool. Then, during setup (before you choose Create pool at the end), edit the standard attributes to your preference.

Important: If you specify new required attributes in the user pool, you must design your Lambda function to provide these new attributes to the new user pool, or authentication fails during user migration. For example, say that you required only email in your old user pool, but now you require both email and phone number in your new user pool. In this case, you must pass attribute values for phone number to your new user pool to successfully authenticate users.

Create a Lambda function

Create a user migration Lambda function using the Lambda console editor or by building and uploading your own deployment package.

Here's an example function in Python that you can use for testing:

Note: For UserPoolId, replace the example value with the ID of the old user pool. Find the ID in the Amazon Cognito console, on the management page for the user pool, on the General settings tab. For ClientId, replace the example value with the app client ID of the old user pool. Find the app client ID in the Amazon Cognito console, too—under General settings, choose App clients.

Important: This sample code won't work to migrate users who use multi-factor authentication (MFA) in the old user pool.

# Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# Permission is hereby granted, free of charge, to any person obtaining a copy of this
# software and associated documentation files (the "Software"), to deal in the Software
# without restriction, including without limitation the rights to use, copy, modify,
# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import json
import boto3

client = boto3.client('cognito-idp')

def lambda_handler(event, context):
    if (event['triggerSource'] == 'UserMigration_Authentication'):
        user = client.admin_initiate_auth(
            UserPoolId='<user pool id of the user pool where the user already exists>',
            ClientId='<app client id of the user pool where the user already exists>',
            AuthFlow='ADMIN_NO_SRP_AUTH',
            AuthParameters={
                'USERNAME': event['userName'],
                'PASSWORD': event['request']['password']
            }
        )
        
        if (user):
            userAttributes = client.get_user(
                AccessToken=user['AuthenticationResult']['AccessToken']
            )
            for userAttribute in userAttributes['UserAttributes']:
                if userAttribute['Name'] == 'email':
                    userEmail = userAttribute['Value']
                    #print(userEmail)
                    event['response']['userAttributes'] = {
                        "email": userEmail,
                        "email_verified": "true"
                    }
            
            event['response']['messageAction'] = "SUPPRESS"
            print (event)
            return (event)
        else:
            return('Bad Password')
    elif (event["triggerSource"] == "UserMigration_ForgotPassword"):
        user = client.admin_get_user(
            UserPoolId='<user pool id of the user pool where the already user exists>',
            Username=event['userName']
        )
        if (user):
            for userAttribute in user['UserAttributes']:
                if userAttribute['Name'] == 'email':
                    userEmail = userAttribute['Value']
                    print(userEmail)
                    event['response']['userAttributes'] = {
                        "email": userEmail,
                        "email_verified": "true"
                    }
            event['response']['messageAction'] = "SUPPRESS"
            
            print (event)
            return (event)
        else:
            return('Bad Password')
            
    else:
        return('there was an error')

Add a user migration trigger to the new user pool

In the Amazon Cognito console, set your new Lambda function as a user migration Lambda trigger. For more information, see Adding a User Pool Lambda Trigger.

Enable the USER_PASSWORD_AUTH flow for user migration

Configure your user pool app client to use the USER_PASSWORD_AUTH authentication flow at the time of migration. This lets your app pass the user's user name and password to the Lambda function and then authenticate them from the existing user pool.

Add logic to your app to change the default authentication flow to the USER_PASSWORD_AUTH flow for sign-in attempts where the user exists in the old user pool and doesn't exist in the new user pool.

For example, if your app uses JavaScript, you'd specify cognitoUser.setAuthenticationFlowType as USER_PASSWORD_AUTH.

Note: After your users are migrated, it's a best practice that you change your app's authentication flow to USER_SRP_AUTH. This flow authenticates users using the Secure Remote Password (SRP) protocol without sending passwords across the network. This flow also provides security benefits over the USER_PASSWORD_AUTH flow. For more information, see User Migration Authentication Flow and Authentication with a User Pool.

cognitoUser.setAuthenticationFlowType('USER_PASSWORD_AUTH');

    cognitoUser.authenticateUser(authenticationDetails, {
        onSuccess: function(result) {
            // User authentication was successful
        },
        onFailure: function(err) {
            // User authentication was not successful
        },
        mfaRequired: function (codeDeliveryDetails) {
            // MFA is required to complete user authentication.
            // Get the code from user and call
            cognitoUser.sendMFACode(verificationCode, this);
        }
    });

For more information, see Example: Migrating JavaScript Users with a Lambda Trigger.

Test the setup

Sign in to your app using the Amazon Cognito hosted web UI to test the authentication flow. The user that you sign in with is authenticated with the new user pool and then migrated.

Note: If you don't have a user account to sign in with for testing, create a new user.

Troubleshooting

If you get an error message such as "Exception during user migration" while testing, enable logging statements from Lambda. This logs the parameters for a user migration Lambda trigger to Amazon CloudWatch Logs.

Reproduce the error, then review the logs for any issues with the parameters or syntax errors in the user migration Lambda trigger.