Amplify-js: lambda is not authorized to perform: cognito-idp:ListUsers

Created on 4 Sep 2018  Â·  5Comments  Â·  Source: aws-amplify/amplify-js

What is the current behavior?
I want to create a lambda function trigger PreSignUp and checks if there are other users already signed up using the same email.

here is my function:

'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);      
    });
  }
};

I used to get such error

lambda is not authorized to perform: cognito-idp:ListUsers

Do you have any suggestion and how to configure the IAM-lambda role.
My current lambda role:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
Cognito

Most helpful comment

Thanks for the response. I was able to get it working in my AWS Amplify project. For anyone else who’s trying to figure it out…

List Users Permission

In an AWS Amplify project they auto-create a bunch of stuff for you and when you create a lambda function amplify add function it ends up in a directory like this:

./amplify/backend/function/<function_name>/

And there’s a Cloudformation file in that directory at <function_name>-cloudformation-template.json. Under Resources.lambdaexecutionpolicy.Properties.PolicyDocument.Statement, I was able to add this additional statement:

{
  "Effect": "Allow",
  "Action": ["cognito-idp:ListUsers"],
  "Resource": {
    "Fn::Sub": [
      "arn:aws:cognito-idp:${region}:${account}:*",
      {
        "region": {
          "Ref": "AWS::Region"
        },
        "account": {
          "Ref": "AWS::AccountId"
        },
        "lambda": {
          "Ref": "LambdaFunction"
        }
      }
    ]
  }
}

Error message in Hosted UI

Using the hosted UI and returning an error with the newer callback syntax, exports.handler = function(event, context, callback) { … }, and if I do callback(Error('a user with the same email address exists')), then I get the error message:

PreSignUp failed with error a user with the same email address exists.

screen shot 2018-12-26 at 11 22 25 am

However, with Social Signin, instead of showing the me error in the UI, it redirects me back to my app to a URL like this:

https://localhost:1234/auth/signin?error_description=PreSignUp+failed+with+error+a+user+with+the+same+email+address+exists.+&error=invalid_request

and I have to handle that in my UI I suppose.

This isn’t ideal, but it’s something workable. Also, I’m flabbergasted that there’s no option to have Cognito pools make emails case-insensitive.

All 5 comments

Here is my solution:

data "aws_iam_policy_document" "pre_signup_policy_doc" {
  statement {
    sid    = ""
    effect = "Allow"

    actions = ["cognito-idp:ListUsers"]

    resources = ["*"]
  }
}

resource "aws_iam_role_policy" "iam_for_presignup_lambda_policy" {
  role = "${aws_iam_role.iam_for_presignup_lambda.id}"
  policy = "${data.aws_iam_policy_document.pre_signup_policy_doc.json}"
  //  policy = <<EOF
  //{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "cognito-idp:ListUsers" ], "Resource": "*" } ] }
  //EOF
}


@engharb can you share some context of how you’d use that data and resource snippet? I see there’s a <functionname>-cloufromation-template.json in my amplify project and maybe I can edit that?

(Also, does that duplicate check work for you, i.e., are you using the hosted UI and does it do something useful when you return that error?)

@mrcoles based on my knowledge this is separated issue. I mean there is no direct relation between configuring your aws-infrastructure than your-amplify-project settings. In order to be able to check i.e the existence of email or username in Cognito-User-Pool using Amplify-js or external API you have to create a policy enabling your Lambda functions to get access/permission to your Cognito-User-Pool (something like that).

And regarding <functionname>-cloufromation-template.json I have no idea what it is.

Honestly I'm not using Hosted UI directly. I used to create my own form and then validate the submitted data using Amplify-js API. And for social login I used to call

https://AUTH_DOMAIN/oauth2/authorize...

Not at all.

Thanks for the response. I was able to get it working in my AWS Amplify project. For anyone else who’s trying to figure it out…

List Users Permission

In an AWS Amplify project they auto-create a bunch of stuff for you and when you create a lambda function amplify add function it ends up in a directory like this:

./amplify/backend/function/<function_name>/

And there’s a Cloudformation file in that directory at <function_name>-cloudformation-template.json. Under Resources.lambdaexecutionpolicy.Properties.PolicyDocument.Statement, I was able to add this additional statement:

{
  "Effect": "Allow",
  "Action": ["cognito-idp:ListUsers"],
  "Resource": {
    "Fn::Sub": [
      "arn:aws:cognito-idp:${region}:${account}:*",
      {
        "region": {
          "Ref": "AWS::Region"
        },
        "account": {
          "Ref": "AWS::AccountId"
        },
        "lambda": {
          "Ref": "LambdaFunction"
        }
      }
    ]
  }
}

Error message in Hosted UI

Using the hosted UI and returning an error with the newer callback syntax, exports.handler = function(event, context, callback) { … }, and if I do callback(Error('a user with the same email address exists')), then I get the error message:

PreSignUp failed with error a user with the same email address exists.

screen shot 2018-12-26 at 11 22 25 am

However, with Social Signin, instead of showing the me error in the UI, it redirects me back to my app to a URL like this:

https://localhost:1234/auth/signin?error_description=PreSignUp+failed+with+error+a+user+with+the+same+email+address+exists.+&error=invalid_request

and I have to handle that in my UI I suppose.

This isn’t ideal, but it’s something workable. Also, I’m flabbergasted that there’s no option to have Cognito pools make emails case-insensitive.

@mrcoles from today new User Pools can be created with case insensitivity for username input
More info [here] (https://aws.amazon.com/about-aws/whats-new/2020/02/amazon-cognito-user-pools-service-now-supports-case-insensitivity-for-user-aliases)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rygo6 picture rygo6  Â·  3Comments

TheRealRed7 picture TheRealRed7  Â·  3Comments

rayhaanq picture rayhaanq  Â·  3Comments

ldgarcia picture ldgarcia  Â·  3Comments

karlmosenbacher picture karlmosenbacher  Â·  3Comments