AWS Amplify:如何为基于电子邮件的身份验证映射社交提供商属性?

AWS Amplify: How to map social providers attributes for an email based authentication?

我有一个使用 AWS Amplify 构建的 React Web 应用程序 我已经使用 Cognito 用户池添加了身份验证,我没有使用用户名,我选择了仅使用 email/phone 的登录名,我不需要用户名,但 Cognito 无论如何都会创建一个随机用户名。

我希望用户使用他们的电子邮件或使用一个社交提供商(Facebook 或 Google)登录,没关系,根据电子邮件,他们应该可以访问同一帐户。

我认为这是很正常的做法。 但是,当我第一次尝试使用社交服务提供商登录时,我注意到 Cognito 没有给我相同的帐户,而是创建了一个新帐户,具有不同的用户名,并且 EXTERNAL_PROVIDER 作为用户池中的帐户状态

所以,我认为这可能只是一些映射不正确,我去了联合部分的属性映射,我看到 Facebook Id 和 Google Sub 被分配给了用户名,我试图删除它,令我惊讶的是,它被分配回了用户名。 然后我想"I can just create a custom attribute to store that information and it should be fine".

所以我这样做了,为 Facebook 创建了一个属性,为 GoogleId 创建了一个属性...再次尝试...不,仍然返回到用户名,但没有错误消息,什么都没有...

我寻求有关文档的帮助并找到了 this

Currently, only the Facebook id, Google sub, login with Amazon user_id, and Sign in with Apple sub attributes can be mapped to the Amazon Cognito User Pools username attribute.

如果这总是将每个提供者 ID 与用户名相关联,则无法合并这些帐户 我认为可能是我需要设置一个联合身份池,但阅读它似乎是用来向外部用户提供 IAM roles/permission,我不想这样做。

知道如何实现吗?

深入研究后,我找到了解决方案。 总之,您应该:

  • 在 Cognito 上为预注册 lambda 函数创建触发器
  • 这个 lambda 函数应该找到相应的帐户和 link 两个用户
  • Return 来自 lambda 函数的事件

您可以从 Amplify cli 创建触发器,运行 amplify auth update,执行 "Walkthrough all the auth configurations",最后,它会询问您是否要创建触发器,确认并 select 预注册触发器

然后编辑创建的函数文件,默认运行时间是nodejs,我已经改成了Python

这是我正在使用的代码

import boto3

client = boto3.client('cognito-idp')


def handler(event, context):
    print("Event: ", event)
    email = event['request']['userAttributes']['email']

    # Find a user with the same email
    response = client.list_users(
        UserPoolId=event['userPoolId'],
        AttributesToGet=[
            'email',
        ],
        Filter='email = "{}"'.format(email)
    )

    print('Users found: ', response['Users'])

    for user in response['Users']:
        provider = None
        provider_value = None
        # Check which provider it is using
        if event['userName'].startswith('Facebook_'):
            provider = 'Facebook'
            provider_value = event['userName'].split('_')[1]
        elif event['userName'].startswith('Google_'):
            provider = 'Google'
            provider_value = event['userName'].split('_')[1]

        print('Linking accounts from Email {} with provider {}: '.format(
            email,
            provider_value
        ))

        # If the signup is coming from a social provider, link the accounts
        # with admin_link_provider_for_user function
        if provider and provider_value:
            print('> Linking user: ', user)
            print('> Provider Id: ', provider_value)
            response = client.admin_link_provider_for_user(
                UserPoolId=event['userPoolId'],
                DestinationUser={
                    'ProviderName': 'Cognito',
                    'ProviderAttributeValue': user['Username']
                },
                SourceUser={
                    'ProviderName': provider,
                    'ProviderAttributeName': 'Cognito_Subject',
                    'ProviderAttributeValue': provider_value
                }
            )
    # Return the event to continue the workflow
    return event