创建一个将 lambda 配置为触发器的 Cognito UserPool

Creating a Cognito UserPool with lambda's configured as triggers

我正在尝试使用 JavaScript SDK 从脚本以编程方式创建 Cognito 用户池。

我已经成功创建了用户池并通过在我的配置中指定相关 lambda 的 arn 定义了预注册和 post-确认触发器。 (根据 docs

我的脚本如下所示:

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

当我 运行 此脚本成功创建用户池时,当我在控制台中检查它时,触发器设置正确。

当我尝试在我的用户池中注册用户时出现错误:

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

如果我进入控制台并手动设置触发器,它就可以正常工作。

已报告此错误 - 但我没有看到确认信息,也没有看到解决方案:

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

绝望地无法修复或找到解决方法。

我设法解决了这个问题。问题是 lambda 没有与 cognito 交互的正确权限。

我发现这段信息被隐藏了 here

因此,在创建用户池的回调函数中,我附加了正确的权限,如下所示:

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

请参阅有关通过 JavaScript SDK 添加权限的文档 here

如果您在云形成中设置了用户池和 lambda 触发器,则需要为用户池添加适当的权限以调用 lambda 函数。

您必须将类似的内容添加到您的云形成模板中。

"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}"
        }
    }
}

如果您需要在 serverless.yml 文件中添加权限,那么这对我们有用。将其添加到您的资源部分:

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/*

这使所有用户池都能够调用您的特定功能。

您可以通过查看 .serverless/serverless-state.json 获取要使用的函数名称,然后在您的 lambda 中您会看到 FunctionName 属性.