Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a Cognito UserPool with lambda's configured as triggers

I am attempting to create a Cognito user pool programmatically from a script using the JavaScript SDK.

I have successfully created the user-pool and defined a pre-signup and post-confirmation trigger by specifying the arn's of the relevant lambdas in my config. (as per the docs)

My script looks like this:

const aws = require('aws-sdk');
const awsConfig = require('../config/config');

aws.config.update({ region: awsConfig.REGION });

const provider = new aws.CognitoIdentityServiceProvider();


// user provided args
const stage = process.argv[2];

if (!stage) {
  process.stdout.write('Please provide stage as argument\n');
  process.exit(1);
}


// generate arns for pre and post cognito triggers
const getArn = (lambdaName) => {
  return `arn:aws:lambda:${awsConfig.REGION}:${awsConfig.AWS_ACCOUNT_ID}` +
         `:function:my-project-name-${stage}-${lambdaName}`;
};

const preSignUp = getArn('preSignUp');
const postConfirmation = getArn('postConfirmation');


const userPoolConfig = {
  PoolName: `mypool-${stage}`,
  AutoVerifiedAttributes: ['email'],
  Schema: [
    {
      "StringAttributeConstraints": {
        "MaxLength": "2048",
        "MinLength": "0"
      },
      "Mutable": true,
      "Required": true,
      "AttributeDataType": "String",
      "Name": "email",
      "DeveloperOnlyAttribute": false
    }
  ],
  LambdaConfig: {
    PostConfirmation: postConfirmation,
    PreSignUp: preSignUp
  }
};


const callback = (err, resp) => {
  if (err) {
    process.stdout.write(`${err}\n`);
  } else {
    process.stdout.write(resp.UserPool.Id);
  }
};


provider.createUserPool(userPoolConfig, callback);

When I run this script it successfully creates the user pool, an and when I inspect it in the console the triggers are set correctly.

When I try to register a user on my user pool I get the error:

AccessDeniedException { code: 'UnexpectedLambdaException', ... }

If I go into the console and set the trigger manually it works just fine.

This bug has been reported - but I see no confirmation, nor solution:

https://github.com/aws/aws-cli/issues/2256

Desperately unable to fix or find a workaround.

like image 617
Sam Redway Avatar asked Mar 21 '17 17:03

Sam Redway


People also ask

What is Cognito sync trigger?

Amazon Cognito raises the Sync Trigger event when a dataset is synchronized. You can use the Sync Trigger event to take an action when a user updates data. The function can evaluate and optionally manipulate the data before it is stored in the cloud and synchronized to the user's other devices.

How do I add a Lambda trigger to a user pool?

In your user pool, choose the Triggers tab from the navigation bar. Choose a Lambda trigger such as Pre sign-up or Pre authentication and choose your Lambda function from the Lambda function drop-down list. Choose Save changes .

What AWS Lambda triggers can I use with Cognito?

You can use AWS Lambda triggers to customize workflows and the user experience with Amazon Cognito. You can create the following Lambda triggers: Pre sign-up, Pre authentication, Custom message, Post authentication, Post confirmation, Define Auth Challenge, Create Auth Challenge, Verify Auth Challenge Response, and User Migration.

How do I create a user pool in Amazon Cognito?

Navigate to the Amazon Cognito console, choose Manage User Pools . Choose an existing user pool from the list, or create a user pool . In your user pool, choose the Triggers tab from the navigation bar.

What is a user migration Lambda trigger?

A user migration Lambda trigger allows easy migration of users from your existing user management system into your user pool. For examples of each Lambda trigger, see Customizing user pool workflows with Lambda triggers. The Custom message AWS Lambda trigger is an advanced way to customize messages for email and SMS.


Video Answer


3 Answers

If you need to add the permission in a serverless.yml file then this is what worked for us. Add it to your Resources section:

UserPoolLambdaInvokePermission:
  Type: AWS::Lambda::Permission
  Properties:
    Action: lambda:invokeFunction
    Principal: cognito-idp.amazonaws.com
    FunctionName: <function-name>
    SourceArn: arn:aws:cognito-idp:<your region>:<your account>:userpool/*

This gives all the user pools the ability to invoke your particular function.

You can get the function name to use by looking in .serverless/serverless-state.json and there against your lambda you'll see the FunctionName property.

like image 97
DenisH Avatar answered Oct 18 '22 01:10

DenisH


If you have set up your user pool and lambda triggers in cloud formation you will need to add the appropirate permissions for the user pool to invoke the lambda function.

You would have to add something like this to your cloud formation template.

"UserPoolPreSignupLambdaInvokePermission"    : {
    "Type" : "AWS::Lambda::Permission",
    "Properties" : {
        "Action" : "lambda:invokeFunction",
        "Principal" : "cognito-idp.amazonaws.com",
        "FunctionName" :{ "Ref" : "AutoVerifyEmailPreSignupLambdaFunction" },
        "SourceArn"    : {
            "Fn::Sub" : "arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${UserPool}"
        }
    }
}
like image 40
David Rees Avatar answered Oct 18 '22 01:10

David Rees


I managed to solve this problem. The issue is that the lambda does not have the correct permissions to interact with cognito.

I found this snippet of information hidden away here

So the in the callback function for the create user pool I attached the correct permissions like this:

const callback = (err, resp) => {
  if (err) {
    process.stdout.write(`${err}\n`);
  } else {

    const userPoolId = resp.UserPool.Id;

    // the lambdas must have a permission attached that allows them to interact
    // directly with cognito
    const generateLambdaPersmission = (userPoolName, lambdaName) => {
      return {
        Action: 'lambda:InvokeFunction',
        Principal: 'cognito-idp.amazonaws.com',
        SourceArn: `arn:aws:cognito-idp:${awsConfig.REGION}:${awsConfig.AWS_ACCOUNT_ID}:userpool/${userPoolId}`,
        FunctionName: getArn(lambdaName),
        StatementId: `${stage}1`
      };
    };

    lambda.addPermission(generateLambdaPersmission(userPoolId, 'preSignUp'), (err, resp) => {
      if (err) {
        process.stdout.write(`error attaching permission to lambda: ${err}`);
      }
    });

    lambda.addPermission(generateLambdaPersmission(userPoolId, 'postConfirmation'), (err, resp) => {
      if (err) {
        process.stdout.write(`error attaching permission to lambda: ${err}`);
      }
    });

    process.stdout.write(userPoolId);
  }
};

See the documentation on adding permissions via the JavaScript SDK here

like image 43
Sam Redway Avatar answered Oct 18 '22 03:10

Sam Redway