I have a React web application built with AWS Amplify I have added authentication with Cognito user pools, I am not using usernames, I have selected the login using email/phone only, I don't want usernames, but Cognito creates a random username anyway.
I want that the user to login using their email or using one social provider (Facebook or Google) and it shouldn't matter, they should have access to the same account, based on the email.
Pretty normal practice, I think. However, when I first tried to log in with a social provider I noticed that instead of Cognito gives me the same account, it created a new one, with a different username and with EXTERNAL_PROVIDER as account status on the user pool
So, I thought it might be just some mapping being made incorrectly, I went to the Attributes mapping on the Federation section, and I saw that the Facebook Id and Google Sub were being assigned to the username, I tried to remove it and for my surprise, it was assigned back to the username. Then I thought "I can just create a custom attribute to store that information and it should be fine".
So I did this, created one attribute for Facebook, one for GoogleId... tried again...nope, still getting back to username, but no error message, nothing...
I went for help on the documentation and found 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.
If this always associate each provider Id with the username, then, there's no way to merge those accounts I thought it could be that I need to setup a federated identity pool, but reading about it it seems that it is used to give IAM roles/permission to the external users, which I don't want to.
Any idea of how can I achieve that?
After digging a bit into it, I found the solution. In summary, you should:
You can create the trigger from Amplify cli, running amplify auth update
, doing the "Walkthrough all the auth configurations", in the end, it is gonna ask you if you want to create the trigger, confirm it and select the Pre Signup Trigger
Then edit the function file created, the default runtime is nodejs
, I have changed mine to Python
This is the code I'm using
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With