【问题标题】:Is it possible to modify AWS Cognito user attributes in the Lambda triggers是否可以在 Lambda 触发器中修改 AWS Cognito 用户属性
【发布时间】:2018-07-07 07:41:57
【问题描述】:

查看 AWS 文档,

https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html#cognito-user-pools-lambda-trigger-syntax-pre-signup

您在 Pre Sign-up Lambda 函数中有以下可用参数:

"request": {
  "userAttributes": {
    "string": "string",
    ....
},
"validationData": {<validation data as key-value (String, String) pairs, from the client>}

有没有办法修改或添加额外的 userAttributes 事件对象?

例如:

// Modify an existing username...
event.request.userAttributes.name.ucfirst();

// Add an additional attribute...
event.request.userAttributes.nickname = "ANY_NAME";


callback(null, event);

【问题讨论】:

  • 你好@dieheld,你找到解决这个问题的方法了吗?
  • 我遇到了同样的问题。除了event.response.autoConfirmUserevent.response.autoVerifyEmailevent.response.autoVerifyPhone这三个可以在预注册中完成之外,我似乎没有办法修改用户属性,但custom:一个都不能修改。没有一个触发器挂钩支持更新 custom: 用户属性 afaik 并基于我的测试。这可能是一个非常理想的功能。

标签: triggers aws-lambda amazon-cognito


【解决方案1】:

是的,绝对有办法!您需要在 Lambda 处理程序中使用 AWS javascript SDK:

const AWS = require('aws-sdk');
AWS.config.update({region: 'ap-southeast-1'});

const cognitoidentityserviceprovider =
  new AWS.CognitoIdentityServiceProvider({
    apiVersion: '2016-04-18'
  });
cognitoidentityserviceprovider.adminUpdateUserAttributes(
  {
    UserAttributes: [
      {
        Name: 'YOUR_USER_ATTRIBUTE_NAME',
        Value: 'YOUR_USER_ATTRIBUTE_VALUE'
      }
    ],
    UserPoolId: event.userPoolId,
    Username: event.userName
  },
  function(err, data) {
    ...
  }
);

确保为您的 Lambda 函数提供正确的策略(即允许“cognito-idp:AdminUpdateUserAttributes”操作)并且用户池已定义属性。

【讨论】:

  • 这在预注册功能中对我不起作用;看起来此时尚未创建用户。有其他人让它工作吗?
  • 由于用户还不存在(使用联合身份进行注册时),这似乎在发布确认触发器中也间歇性失败
【解决方案2】:

在注册期间无法更改/增强属性,但在登录期间,您可以使用 pre-token generation trigger 更改/增强属性。

【讨论】:

  • 我怀疑这是正确的答案,因为其他解决方案都不适合我。
  • 它是否也适用于 SAML 身份提供者?我注意到,SAMLResponse 属性的更改不会更新用户池中的用户属性。
  • 这只会更改 Cognito 管理的身份令牌。例如,当在 API Gateway 中使用 authorizer.context.fooBar 时,您所做的更改将不会发生,因此您必须小心这样做。
  • 你能不能也看看这个问题。提前致谢! stackoverflow.com/questions/68367111/…
【解决方案3】:

对于其他希望深入了解这个问题的人,下面是一个示例

下面的 lambda 函数 #1 包含两个自定义属性 idaethaddress。在 Cognito 用户池的 PreSignUpHook 期间调用 lambda

#2(在事件更改日志之前)这些属性的原始值为 ida=1ethaddress=ABCD

#3(事件更改后的日志)反映了这些属性的更改值: ida=2ethaddress=EFGH

但是,保存到 cognito 的值是原始值:ida=1ethaddress=ABCD。因此,在 presignuphook 期间更新 userAttributes 并不像某些答案中所建议的那样工作。

附带说明,当响应对象中的预定义属性被修改时,它们会按预期更新:

"response": {
    "autoConfirmUser": true,
    "autoVerifyEmail": false,
    "autoVerifyPhone": false
}
1.拉姆达:
'use strict';
global.fetch = require('node-fetch')

module.exports.preSignUp = async (event, context, callback) => {
// Set the user pool autoConfirmUser flag after validating the email domain

let data = await fetch("http://***.***.***/api/members/create",
{
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    method: "POST",
})
.then(res => res.json())
.then(res => res);

event.response.autoConfirmUser = true;
console.log('before event:', JSON.stringify(event)); 
event.request.userAttributes['custom:ethaddress'] = String(data.address); 
event.request.userAttributes['custom:ida'] = "2";  
console.log('Received event:', JSON.stringify(event));  
console.log('Address:', data.address);


 // Return to Amazon Cognito
callback(null, event);
 };
2.

事件更改日志之前:

2019-01-20T01:02:24.639Z    edce636e-75ea-492b-b6a0-dd4f22dc9038    before event:
{
    "version": "1",
    "region": "us-east-1",
    "userPoolId": "us-east-1-*****",
    "userName": "*******@gmail.com",
    "callerContext": {
        "awsSdkVersion": "aws-sdk-unknown-unknown",
        "clientId": "******************"
    },
    "triggerSource": "PreSignUp_SignUp",
    "request": {
        "userAttributes": {
            "custom:ida": "1",
            "custom:ethaddress": "ABCD",
            "email": "*******@gmail.com"
        },
        "validationData": {}
    },
    "response": {
        "autoConfirmUser": true,
        "autoVerifyEmail": false,
        "autoVerifyPhone": false
    }
}
3.

事件更改日志之后:

Received event:
{
    "version": "1",
    "region": "us-east-1",
    "userPoolId": "us-east-1_0BaE6eaTY",
    "userName": "*******@gmail.com",
    "callerContext": {
        "awsSdkVersion": "aws-sdk-unknown-unknown",
        "clientId": "*****************"
    },
    "triggerSource": "PreSignUp_SignUp",
    "request": {
        "userAttributes": {
            "custom:ida": "2",
            "custom:ethaddress": "EFGH",
            "email": "*******@gmail.com"
        },
        "validationData": {}
    },
    "response": {
        "autoConfirmUser": true,
        "autoVerifyEmail": false,
        "autoVerifyPhone": false
    }
}

更新:

似乎没有办法将其作为 PRESIGNUP 流程的一部分 但是,可以在下面提供的 cognito 示例中将其作为 POSTCONFIRMATION 触发器来执行。

一些注意事项。

  1. 自定义属性已添加到 cognito 中并且是可变的。
  2. 在App客户端-->显示详情-->“设置属性读写权限” 确保自定义属性具有以下读写权限。
  3. 确保 lambda 函数具有允许其执行的角色:adminUpdateUserAttributes 例如。将 AmazonCognitoPowerUser 策略附加到 LambaRole。
module.exports.postConfirmation = async (event, context,callback) => {
        const cognitoIdServiceProvider = new CognitoIdentityServiceProvider({
          region: 'us-east-1'
        });

        var params =  {
            UserAttributes: [
              {
                  Name: 'custom:sillyName',
                  Value: 'customSillyName'
              }
            ],
            UserPoolId: event.userPoolId,
            Username: event.userName
        }

        cognitoIdServiceProvider.adminUpdateUserAttributes(params, function(err, data) {
          if (err) console.log(err, err.stack); // an error occurred
          else     console.log(data);           // successful response
        }); 

        callback(null,event);

};

请注意,如果您尝试在 preSignUp 触发器钩子中使用 cognitoIdServiceProvider.adminUpdateUserAttributes,您将收到一个异常,说明用户尚未退出

【讨论】:

  • 不。我已经测试了注册前和确认后触发器,在这两种情况下,错误消息都是"errorMessage": "User does not exist." 在这两个阶段,用户显然仍未在池中创建,因此无法使用 SDK 更新属性蜜蜂。在注册前触发器中将更新的属性注入event.request.userAttributes 似乎也不起作用。唯一可以做的是验证类型的东西和其他东西,比如日志记录等。
  • 我发现这适用于 Post Auth 触发器并稍作调整,我需要将 callback(null, event) 移动到 cognitoIdServiceProvider.adminUpdateUserAttributes() 回调函数中。
【解决方案4】:

要围绕@Khoi 的非常有用的答案填写一些细节,并且对于所有剪贴画者(您知道自己是谁),这里有一个 Lambda 模板,该模板从 Cognito 用户池发布确认触发器运行。

Lambda 在用户的自定义属性中设置一个新值,在本示例中为“fruit”。以下是在实施雷区中要避免的一些陷阱:

  1. 自定义属性必须在创建用户池时定义在用户池中,定义时必须设置属性Mutable。李>
  2. 将 Lambda 部署到您的 AWS 账户后,您需要通过 AWS 控制台中的用户池配置页面将发布确认触发器指向此 Lambda。
    服务->Cognito->管理用户池- >您的用户池->触发器->发布确认
  3. Lambda 需要更新用户池属性的权限,这是通过将 IAM 策略附加到允许“cognito-idp:AdminUpdateUserAttributes”操作的 Lambda 来完成的。请参见下面的示例。
  4. 您访问带有前缀 custom: 的属性,就像在 custom:fruit 中一样。
  5. 自定义属性中的冒号使 Javascript 不愉快,因此当从 CognitoUser 对象访问客户端中的自定义属性时,例如来自您需要使用的 Amplify signIn() 方法
    let a = user.attributes['custom:fruit']

示例 Lambda

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

    // Cognito User Pool Lambda triggers are documented here:
    // https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html
    exports.lambdaHandler = async (event, context, callback) => {

        const params = {
            UserPoolId: event.userPoolId,
            Username: event.userName,
            UserAttributes:  // this parameter needs to be an array
                [
                    {
                        Name: 'custom:fruit',
                        Value: 'banana'
                    }
                ]
        };

        if (event.request.userAttributes.email) {
            try {
                await cisProvider
                    .adminUpdateUserAttributes(params)
                    .promise();

                console.log('Success');
            } catch (error) {
                console.error('Error', error);
            }
        }

        callback(null, event);
    };

SAM YAML 模板

如果您使用 AWS SAM(如我所愿)来调试和部署您的 Lambda,这里是此 Lambda 的模板。注意 Role 资源。在部署 Lambda 之前,您需要在 AWS 控制台中定义此 IAM 角色。可能有一种方法可以在模板中在这里定义角色,但我不是 AWS YAML 专家。使用 SAM CLI 命令将 Lambda 部署到您的 AWS 账户
sam deploy --guided

  AWSTemplateFormatVersion: '2010-09-09'
  Transform: AWS::Serverless-2016-10-31
  Description: >
    SAM Template for lambda function that runs as a Cognito User Pool post confirmation trigger.
    Cognito invokes this function when a new user signs up.
    
  Globals:
    Function:
      Timeout: 3

  Resources:
    PostConfirmationFunction:
      Type: AWS::Serverless::Function
      Properties:
        CodeUri: post-confirmation/
        Handler: app.lambdaHandler
        Runtime: nodejs14.x
        Role:
          # This role gives Lambda permission to update user pool attributes as well as basic execution.
          arn:aws:iam::xxxxxxxxxxxx:role/lambda-cognito-update-role

  Outputs:
    PostConfirmationFunction:
      Description: "Post Confirmation Lambda Function ARN"
      Value: !GetAtt PostConfirmationFunction.Arn

IAM 角色

您需要在 IAM 控制台中创建一个角色,其中包含更新用户属性的权限。我更喜欢使用“内联策略”来执行此操作,以避免我的帐户中的 IAM 策略激增且依赖关系不明确。我发现做到这一点的最佳方法是两步:

  1. 在 IAM 控制台中创建一个与 YAML 模板中列出的名称相同的角色,在此示例中为 lambda-cognito-update-role。在 Permissions 步骤中附加 AWSLambdaBasicExecutionRole,并为其赋予角色名称 lambda-cognito-update-role
  2. 在 AWS 控制台的 IAM 部分的角色下,找到您新创建的角色并单击以将其打开。
    • 当您在此处时,将“摘要”页面顶部的角色 ARN 复制到您的 SAM YAML 模板中。
    • 点击右侧的Add inline policy,点击JSON选项卡,用下面的JSON覆盖样板:
{
  "Version": "2012-10-17",
  "Statement": [
    {
        "Sid": "CognitoUpdate",
        "Effect": "Allow",
        "Action": "cognito-idp:AdminUpdateUserAttributes",
        "Resource": "*"
    }
  ]
}

可以说资源应该更具体,可能仅限于您帐户中的用户池。我让您自行决定。

【讨论】:

    【解决方案5】:

    当然可以。您需要使用 AWS SDK。

    const AWS = require('aws-sdk');
    const config = require('./config'); 
    
    function updateAttribute(params) {
        AWS.config.update({
            'region' : config.AWSConfig.region,
            'accessKeyId': config.AWSConfig.accessKeyId,
            'secretAccessKey': config.AWSConfig.secretAccessKey
        });
        let cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider();
    
        let parameters = { UserPoolId : config.userPoolDetails.userPoolId,
        Username : params.userName,
        UserAttributes : [
            {
                'Name': params.nameOfAttribute ,
                'Value': params.newValueOfAttribute
            },
        ]}
        
        cognitoIdentityServiceProvider.adminUpdateUserAttributes(parameters,function (err, result) {
            if(err)
            console.log(err);
            else
            console.log("Attribute updated successfully");
        })
    }
    
    let params = {
        userName : 'username',
        nameOfAttribute : 'name',
        newValueOfAttribute : 'Sachin'
    }
    
    updateAttribute(params);
    
    

    你甚至可以像这样添加新属性。

    您可以在此处阅读更多内容: https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminUpdateUserAttributes.html

    【讨论】:

      【解决方案6】:

      嗯,简单的解决方案是这样的, 将此添加到“预注册 Lambda 函数”中,从您的代码中获取提示:

      // Modify an existing username...
       event['request']['userAttributes']['name'] = "My_NAME";
      
      // Add an additional attribute...
       event['request']['userAttributes']['custom:sillyname'] = "ANY_NAME";
      
       callback(null, event);
      

      考虑到您已经为用户池添加了custom:sillyname 属性。

      【讨论】:

      • 或使用event.request.userAttributes['custom:sillyname'] = "ANY_NAME" ;
      • 当我尝试这个时,我得到一个失败的“InvalidLambdaResponseException”;你能确认这个功能正常吗?
      • 你在哪个触发器中使用 lambda 函数?
      • @Shaw 这可能是因为您在函数末尾有一个 return 语句,而不是仅仅调用回调。
      • 我很确定您不能在预注册 Lambda 中执行此操作,因为该用户尚不存在。至少在我的测试中,我从来没有能够在这个阶段让它工作。
      猜你喜欢
      • 2021-12-10
      • 2018-04-02
      • 2023-01-01
      • 2016-09-22
      • 2019-06-12
      • 2018-03-11
      • 2023-03-29
      • 2021-05-20
      • 1970-01-01
      相关资源
      最近更新 更多