Cognito auth flow fails with “Already found an entry for username Facebook_10155611263153532”

后端 未结 4 1649
悲&欢浪女
悲&欢浪女 2020-12-24 14:20

The goal is to implement a social provider auth flow as described in User Pools App Integration and Federation.

One important thing that I want to satisfy, is to merg

相关标签:
4条回答
  • 2020-12-24 14:26

    To elaborate on @agent420's answer, this is what I am currently using (Typescript example).

    When a social identity attempts to sign up and the email address already exists I catch this using the PreSignUp trigger and then return an error message to the user. Inside the app, on the user's profile page, there is an option to link an identity provider which calls the adminLinkProviderForUser API.

    import {
        Context,
        CognitoUserPoolTriggerEvent,
        CognitoUserPoolTriggerHandler,
    } from 'aws-lambda';
    import * as aws from 'aws-sdk';
    import { noTryAsync } from 'no-try';
    
    export const handle: CognitoUserPoolTriggerHandler = async (
        event: CognitoUserPoolTriggerEvent,
        context: Context,
        callback: (err, event: CognitoUserPoolTriggerEvent) => void,
    ): Promise<any> => {
        context.callbackWaitsForEmptyEventLoop = false;
    
        const { email } = event.request.userAttributes;
    
        // pre sign up with external provider
        if (event.triggerSource === 'PreSignUp_ExternalProvider') {
            // check if a user with the email address already exists
    
            const sp = new aws.CognitoIdentityServiceProvider();
    
            const { error } = await noTryAsync(() =>
                sp
                    .adminGetUser({
                        UserPoolId: 'your-user-pool-id',
                        Username: email,
                    })
                    .promise(),
            );
    
            if (error && !(error instanceof aws.AWSError)) {
                throw error;
            } else if (error instanceof aws.AWSError && error.code !== 'UserNotFoundException') {
                throw error;
            }
        }
    
        callback(null, event);
    };
    
    
    0 讨论(0)
  • 2020-12-24 14:29

    The same code in JavaScript getUser has been called instead of listUsers. It is also assumed that all users have their email id as their username.

    const aws = require('aws-sdk');
    
    exports.handler = async (event, context, callback) => {
        console.log("event" + JSON.stringify(event));
        const cognitoidentityserviceprovider = new aws.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});
        const emailId = event.request.userAttributes.email
        const userName = event.userName
        const userPoolId = event.userPoolId
        var params = {
            UserPoolId: userPoolId,
            Username: userName
        };
        var createUserParams = {
            UserPoolId: userPoolId,
            Username: emailId,
            UserAttributes: [
                {
                    Name: "email",
                    Value: emailId
                },
            ],
            TemporaryPassword: "xxxxxxxxx"
        };
    
        var googleUserNameSplitArr = userName.split("_");
        var adminLinkUserParams = {
            DestinationUser: {
                ProviderAttributeName: 'UserName',
                ProviderAttributeValue: emailId,
                ProviderName: "Cognito"
            },
            SourceUser: {
                ProviderAttributeName: "Cognito_Subject",
                ProviderAttributeValue: googleUserNameSplitArr[1],
                ProviderName: 'Google'
            },
            UserPoolId: userPoolId
        };
    
        var addUserToGroupParams = {
            GroupName: "Student",
            UserPoolId: userPoolId,
            Username: emailId
        };
    
        if (userName.startsWith("Google_")) {
            await cognitoidentityserviceprovider.adminGetUser(params, function (err, data) {
                if (err) {
                    console.log("No user present")
                    console.log(err, err.stack);
                    cognitoidentityserviceprovider.adminCreateUser(createUserParams, function (err, data) {
                        if (err) console.log(err, err.stack);
                        else {
                            console.log("User Created ")
                            cognitoidentityserviceprovider.adminAddUserToGroup(addUserToGroupParams, function (err, data) {
                                if (err) console.log(err, err.stack);
                                else {
    
                                    console.log("added user to group");
                                    console.log(data);
                                }
                            });
                            cognitoidentityserviceprovider.adminLinkProviderForUser(adminLinkUserParams, function (err, data) {
                                if (err) console.log(err, err.stack);
                                else {
                                    console.log("user linked");
                                    console.log(data);
                                }
                            });
                            console.log(data);
                        }
                    });
                } else {
                    console.log("user already present")
                    cognitoidentityserviceprovider.adminLinkProviderForUser(adminLinkUserParams, function (err, data) {
                        if (err) console.log(err, err.stack); // an error occurred
                        else {
                            console.log("userlinked since user already existed");
                            console.log(data);
                        }
                    });
                    console.log(data);
                }
            });
        }
        console.log("after the function custom");
        callback(null, event);
    };
    
    0 讨论(0)
  • 2020-12-24 14:33

    For all the poor souls fighting with this issue still in 2020 the same way I did:

    • I have eventually fixed the issue by catching the "Already found an entry for username" in my client application and repeating the entire auth flow once more.
    • Luckily the error only gets fired on the initial external provider signup but not in the subsequent signins of the same user (cause it happens during signup trigger, duh). I'm taking a wild guess, but here is what I think is happening:
      • In my case, the facebook provider was getting succesfully linked with the pre-existing cognito email/password user. new Facebook userpool entry linking to the email/password user was succesfully created.
      • Still, it seems like cognito tried to register the fully isolated Facebook_id user during the internal signup process (even though a link user entry with the same username was already created in the previous step). Since the "link user" with the username Facebook_id was already existing, cognito threw an "Already found an entry for username Facebook_id error" internal error.
      • This error has been repeatedly voiced over to the AWS developers since 2017 and there are even some responses of them working on it, but in 2020, it's still not fixed.
    0 讨论(0)
  • 2020-12-24 14:35

    Yes, this is how it is currently setup. If you try to link users using PreSignUp trigger, the first time won't work. A better way to handle this(I think) would be to provide an option in your UI to link external accounts on sign-in. In the pre-signup trigger, search for a user with the same unique attribute (say email) and see if the sign up is from external provider. Then show a message such as email already exists. Login in & use this menu/option to link. Haven't tested this though.

    0 讨论(0)
提交回复
热议问题