AWS Cognito adminCreateUser from Lambda duplicated username added

Viewed 1109

I'm trying to add a new user from lambda nodejs, but every time that I try to create a user it adds it two times in Cognito and get me the error below.

Any comments or suggestions will be highly appreciated.

this is my Lambda Test

var AWS = require('aws-sdk');

AWS.config.update({
    region: 'ca-central-1',
    credentials: new AWS.CognitoIdentityCredentials({
        IdentityPoolId: IdentityPoolId,
    })
});

const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider(
    {
        apiVersion: '2016-04-18',
        region: "ca-central-1",
        credentials: AWS.Credentials()
    }
);

module.exports.getCongito = getCongito;

async function getCongito() {
    
    var info = {
        UserPoolId: UserPoolId.prod,
        Username: 'test@gmail.com',
        TemporaryPassword: "Password1@#",

        UserAttributes: [
             {
                Name: 'name', /* required */
                Value: 'Name1' 
            },
             {
                Name: 'email', /* required */
                Value: 'test@gmail.com'
            },
             {
                Name: 'email_verified', /* required */
                Value: 'true'
            }
        ]
    }

    var params = {
        UserPoolId: info.UserPoolId, /* required */
        Username: info.Username,
        // TemporaryPassword: info.TemporaryPassword,/* required */
        DesiredDeliveryMediums: ["EMAIL"],
        UserAttributes: info.UserAttributes,
        ForceAliasCreation: false
        // 
        
    };

    await cognitoidentityserviceprovider.adminCreateUser(params, function (err, data) {
        if (err) console.log(err, err.stack); // an error occurred
        else console.log(data);           // successful response
    }).promise();

}

this is my pre-signup lambda to avoid duplicate email/username

'use strict';

const AWS = require('aws-sdk');
const cognitoIdp = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});

exports.handler = function(event, context) {
  console.log(JSON.stringify(event));

  // check if email is already in use
  if (event.request.userAttributes.hasOwnProperty('email')) {
    const email = event.request.userAttributes.email;
    
    const params = {
      UserPoolId: event.userPoolId,
      Filter: 'email = "' + email + '"',
    };
    
    cognitoIdp.listUsers(params).promise()
    .then (results => {
      console.log(JSON.stringify(results));
      // if the usernames are the same, dont raise and error here so that
      // cognito will raise the duplicate username error
      if (results.Users.length > 0 && results.Users[0].Username !== event.userName) {
        console.log('Duplicate email address in signup. ' + email);
        context.done(Error('A user with the same email address exists'));
      }
     
      context.done(null, event);
    })
    .catch (error => {
      console.error(error);
      context.done(error);      
    });
  }
};

This what I get when I run it, and when I go to Cognito user pools I see the two users:

{
  "errorType": "InvalidParameterException",
  "errorMessage": "Alias entry already exists for a different username",
  "trace": [
    "InvalidParameterException: Alias entry already exists for a different username",
    "    at Request.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/json.js:52:27)",
    "    at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:106:20)",
    "    at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:78:10)",
    "    at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:688:14)",
    "    at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10)",
    "    at AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12)",
    "    at /var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10",
    "    at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:38:9)",
    "    at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:690:12)",
    "    at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:116:18)"
  ]
}

Thanks,

1 Answers

A couple of things... when you set the email as sign-in alias, cognito forces it to be unique. No need to duplicate the check.

The source of your problem is that you are passing a callback function and then turning it into a promise.

await cognitoidentityserviceprovider.adminCreateUser(params, function (err, data) {
        if (err) console.log(err, err.stack); // an error occurred
        else console.log(data);           // successful response
    }).promise();

Just use the promise.

Below is an implementation. I cleaned up your code somewhat.

const AWS = require('aws-sdk');

/**
 * Grab the user pool id and region from environment variables
 */
const { 
    USER_POOL_ID = 'ca-central-1_xxxxxxxxx',
    AWS_REGION = 'ca-central-1'
} = process.env

/**
 * Set the AWS region
 */
AWS.config.update({
    region: AWS_REGION
});

/**
 * Create the Cognito IDP client
 */
const cognito = new AWS.CognitoIdentityServiceProvider();


/**
 * Create a test user for a given email
 * @param {string} email email to use for creating a test user. Defaults to 'test@example.com'
 */
 module.exports.createTestUser = async function createTestUser(email = 'test@example.com') {

    const params = {
        UserPoolId: USER_POOL_ID,
        Username: email,
        DesiredDeliveryMediums: ["EMAIL"],
        UserAttributes:  [
            {
               Name: 'name',
               Value: 'Test User' 
           },
            {
               Name: 'email',
               Value: email
           },
            {
               Name: 'email_verified',
               Value: 'true'
           }
       ]
    };

    const result = await cognito.adminCreateUser(params).promise()
    console.log(JSON.stringify(result, undefined, 2))
    return result
}
Related