【问题标题】:AWS Cognito: email unverified on main account after AdminLinkProviderForUserAWS Cognito:在 AdminLinkProviderForUser 之后主账户上的电子邮件未经验证
【发布时间】:2021-10-21 19:46:13
【问题描述】:

我正在实现 cognito 中具有相同电子邮件的用户帐户的链接。因此,如果有人注册,例如使用 Google 并且电子邮件已经在 cognito 中,我将使用 AdminLinkProviderForUser 将这个新帐户链接到现有帐户。我基本上一直在这里关注这个答案:https://stackoverflow.com/a/59642140/13432045。链接按预期工作,但之后email_verified 切换到false(之前已验证)。这是预期的行为吗?如果是,那么我的问题是为什么?如果不是,那么我的问题是我做错了什么?这是我的预注册 lambda:

const {
  CognitoIdentityProviderClient,
  AdminLinkProviderForUserCommand,
  ListUsersCommand,
  AdminUpdateUserAttributesCommand,
} = require("@aws-sdk/client-cognito-identity-provider");

exports.handler = async (event, context, callback) => {
  if (event.triggerSource === "PreSignUp_ExternalProvider") {
    const client = new CognitoIdentityProviderClient({
      region: event.region,
    });

    const listUsersCommand = new ListUsersCommand({
      UserPoolId: event.userPoolId,
      Filter: `email = "${event.request.userAttributes.email}"`,
    });

    try {
      const data = await client.send(listUsersCommand);
      if (data.Users && data.Users.length) {
        const [providerName, providerUserId] = event.userName.split("_"); // event userName example: "Facebook_12324325436"
        const provider = ["Google", "Facebook", "SignInWithApple"].find(
          (p) => p.toUpperCase() === providerName.toUpperCase()
        );
        const linkProviderCommand = new AdminLinkProviderForUserCommand({
          DestinationUser: {
            ProviderAttributeValue: data.Users[0].Username,
            ProviderName: "Cognito",
          },
          SourceUser: {
            ProviderAttributeName: "Cognito_Subject",
            ProviderAttributeValue: providerUserId,
            ProviderName: provider,
          },
          UserPoolId: event.userPoolId,
        });

        await client.send(linkProviderCommand);

        /* fix #1 - this did not help */
        // const emailVerified = data.Users[0].Attributes.find(
        //   (a) => a.Name === "email_verified"
        // );
        // if (emailVerified && emailVerified.Value) {
        //   console.log("updating");
        //   const updateAttributesCommand = new AdminUpdateUserAttributesCommand({
        //     UserAttributes: [
        //       {
        //         Name: "email_verified",
        //         Value: "true",
        //       },
        //     ],
        //     UserPoolId: event.userPoolId,
        //     Username: data.Users[0].Username,
        //   });

        //   await client.send(updateAttributesCommand);
        // }

        /* fix #2 - have no impact on the outcome */
        // event.response.autoConfirmUser = true;
        // event.response.autoVerifyEmail = true;
      }
    } catch (error) {
      console.error(error);
    }
  }

  callback(null, event);
};

如您所见,我尝试传递 autoConfirmUserautoVerifyEmail,但没有任何影响。而且我还尝试在调用AdminLinkProviderForUser 之后手动更新email_verified,这也没有帮助。所以我认为 email_verified 只有在 lambda 完成后才设置为 false

【问题讨论】:

    标签: amazon-web-services aws-lambda amazon-cognito federated-identity


    【解决方案1】:

    我实际上已经找到了解决这个问题的方法!这很棘手,但效果很好。

    我遇到的主要问题是,如果用户使用连接的提供商帐户登录,pre/post auth lambdas 不会触发。在注册链接之后也不会触发后确认 lambda。最重要的是,如果您在 cognito 中手动尝试更改提供者的用户,您仍然会得到 email_verified = false

    那么这里的解决方案是什么?

    嗯,实际上是二合一。

    1。属性映射

    Cognito 支持的一些提供程序已经具有“电子邮件验证”属性,我们可以直接映射到 Attribute Mapping 部分。 Google 就是这种情况。只需将其映射到 Cognito 的属性即可!

    2。 PreToken 触发器

    对于其他提供商,主要是 Facebook(我没有使用除 Facebook 和 Google 之外的任何提供商,但它应该都一样),它们本身没有电子邮件验证属性,这是我能找到正确的唯一方法通过预令牌触发器强制对其进行验证。逻辑如下:一旦调用了 lambda,验证通过身份验证的用户是否有链接到它的提供程序,同时,将其 email_verified 选项设置为 false。如果您满足此条件,请在返回 Cognito 之前使用 adminUpdateUserAttributes 方法更新用户。这样一来,任何后续提供者登录是否将属性翻转回 false 都无关紧要,这个 lambda 确保它被翻转回来。

    如果您想要解决方案的示例代码,这是我的预令牌处理程序:

    const AWS = require('aws-sdk');
    
    class PreTokenHandler {
      constructor({ cognitoService }) {
        this.cognitoService = cognitoService;
      }
    
      async main(event, _context, callback) {
      const {
          userPoolId,
          userName: Username,
          request: { userAttributes: { identities, email_verified } }
        } = event;
    
        const emailVerified = ['true', true].some(value => value === email_verified);
    
        if (!identities?.length || emailVerified) return callback(null, event);
    
        await this.cognitoService.adminUpdateUserAttributes({
          UserPoolId: userPoolId,
          Username,
          UserAttributes: [{ Name: 'email_verified', Value: 'true' }]
        }).promise();
    
        callback(null, event);
      }
    }
    
    const cognitoService = new AWS.CognitoIdentityServiceProvider({ apiVersion: '2016-04-18' });
    
    const handler = new PreTokenHandler({ cognitoService });
    
    module.exports = handler.main.bind(handler);
    
    

    我添加了 'true' x true 检查以确保我在属性值可能不一致时是安全的,尽管它只是更加安全并且可能没有必要。

    【讨论】:

    • 感谢您的回复。我最终只使用了您的第二种解决方案。因为它解决了所有提供商的问题。但是我仍然很好奇为什么 email_verified 首先被 cognito 翻转...顺便说一句,您的 'true' x true 检查根本没有用,因为 cognito 将用户属性作为字符串发送。
    【解决方案2】:

    我的回答和@fabio一样,下面是aws-sdk-js-v3的做法:

    const {
      CognitoIdentityProviderClient,
      AdminUpdateUserAttributesCommand,
    } = require('@aws-sdk/client-cognito-identity-provider')
    const client = new CognitoIdentityProviderClient({
      region: process.env.REGION,
    })
    
    exports.handler = async(event, context, callback) => {
    
      try {
    
        const {
          userPoolId,
          userName,
          request: {
            userAttributes: { email_verified }
          }
        } = event
    
        if (!email_verified) {
    
          const param = {
            UserPoolId: userPoolId,
            Username: userName,
            UserAttributes: [{
              Name: 'email_verified',
              Value: 'true',
            }],
          }
          await client.send(new AdminUpdateUserAttributesCommand(param))
    
        }
        callback(null, event)
      }
      catch (err) {
        console.error(err)
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-02-17
      • 2018-03-02
      • 2020-09-30
      • 2019-08-22
      • 2019-10-07
      • 2020-08-24
      • 1970-01-01
      • 2021-03-26
      • 2016-01-17
      相关资源
      最近更新 更多