AWS Cognito: Best practice to handle same user (wi

2020-06-04 12:31发布

问题:

When signing in a user with the same email address through the Google and Facebook identity providers, AWS Cognito creates multiple entries in the user pool, one entry per identity provider used:

I have used the example code provided in this tutorial to set up AWS Cognito: The Complete Guide to User Authentication with the Amplify Framework

  • How can I create just one user instead of multiple users?
  • Is it possible to have AWS Cognito automatically combine (federate) the entries from multiple providers into one entry or should AWS Lambda functions be used to accomplish this?

回答1:

Yes. You can do it by using AdminLinkProviderForUser https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminLinkProviderForUser.html

The idea is:

  1. In PreSignUp lambda hook, we Link Provider to User if User already signed up. E.g:
import CognitoIdentityServiceProvider from 'aws-sdk/clients/cognitoidentityserviceprovider'

const cognitoIdp = new CognitoIdentityServiceProvider()
const getUserByEmail = async (userPoolId, email) => {
 const params = {
   UserPoolId: userPoolId,
   Filter: `email = "${email}"`
 }
 return cognitoIdp.listUsers(params).promise()
}

const linkProviderToUser = async (username, userPoolId, providerName, providerUserId) => {
 const params = {
   DestinationUser: {
     ProviderAttributeValue: username,
     ProviderName: 'Cognito'
   },
   SourceUser: {
     ProviderAttributeName: 'Cognito_Subject',
     ProviderAttributeValue: providerUserId,
     ProviderName: providerName
   },
   UserPoolId: userPoolId
 }

 const result = await (new Promise((resolve, reject) => {
   cognitoIdp.adminLinkProviderForUser(params, (err, data) => {
     if (err) {
       reject(err)
       return
     }
     resolve(data)
   })
 }))

 return result
}

exports.handler = async (event, context, callback) => {
 if (event.triggerSource === 'PreSignUp_ExternalProvider') {
   const userRs = await getUserByEmail(event.userPoolId, event.request.userAttributes.email)
   if (userRs && userRs.Users.length > 0) {
     const [ providerName, providerUserId ] = event.userName.split('_') // event userName example: "Facebook_12324325436"
     await linkProviderToUser(userRs.Users[0].Username, event.userPoolId, providerName, providerUserId)
   } else {
     console.log('user not found, skip.')
   }

 }
 return callback(null, event)
}
  1. Then when user use OAuth with Facebook/Google with User Pool, the Pool will return this User linked.

Note: You may see 2 records in User Pool UI, but when access User record detail, They already merged.