strongloop / loopback-component-passport

LoopBack passport integration to support third party logins and account linking
Other
139 stars 228 forks source link

How to link the same user's accounts (same email) instead of duplicating it? #266

Closed orszaczky closed 4 years ago

orszaczky commented 6 years ago

Hello, I cloned the loopback-example-passport repo, and set it up for local and facebook login. I have 2 use cases where the library acts differently than I would expect.

When I create a local account with username and email {id:1, email: test@email.com}, I can link my FB account to it, and it gets stored in the db under userCredentials. Then i try to log in with FB, and instead of logging me in to my account (id:1) it creates a new account {id: 2, email: userId@loopback.facebook.com}, and logs me into this new account, and it creates a userIdentity entry. Then again, I can link my FB account to this account, and it adds a new entry under userCredentials.

So now i end up with 2 different accounts that has the same FB account (and same email address) linked to. Using FB login I can only log in to the second one, (id:2) using email address i can only log into the first one (id:1).

Obviously this is very confusing and this is not what I want. 1) If I have an existing local account with a username/email, and I link a FB account to it, I want to be able to log in to the same account (id:1) using FB login. 2) If i have a facebook login created first (and has an email linked to it through facebook), and I subsequently sign up using the local login (using an email that is already associated with a FB account), then I want to be able to link the 2 accounts, and log into the same originally created account using the local credentials.

I followed the setup directions, and also checked the documentation, but I didn't find anything explaining this. Not sure if it's a configuration issue, or a bug. I found a similar issue from 2 years ago, https://github.com/strongloop/loopback-component-passport/issues/179 that just got closed without any solution.

Also found another thread https://groups.google.com/forum/#!searchin/loopbackjs/passport|sort:date/loopbackjs/9y_RHRw1pVI/XAu1IjdWAAAJ about this, again without any solution.

cadesalaberry commented 5 years ago

To achieve what you want you should look at overriding the profileToUser(provider, profile) function.

The default one does the following: https://github.com/strongloop/loopback-component-passport/blob/74abd781c8c99bfc8863e3387aca2fbc6fa710ff/lib/models/user-identity.js#L58-L92

If you replace it with a function that looks like this:

function profileToUser(provider, profile, options) {
  return {
    firstName: profile.name.givenName,
    lastName: profile.name.familyName,
    email: profile.email,
  }
}

The passport component will properly match the player with its email and not duplicate the account.

ghost commented 5 years ago

To achieve what you want you should look at overriding the profileToUser(provider, profile) function.

The default one does the following: loopback-component-passport/lib/models/user-identity.js

Lines 58 to 92 in 74abd78

function profileToUser(provider, profile, options) { var password, userObj;

if (!options.profileMapping) { // Let's create a user for that var profileEmail = profile.emails && profile.emails[0] && profile.emails[0].value; var generatedEmail = (profile.username || profile.id) + '@loopback.' + (profile.provider || provider) + '.com'; var email = provider === 'ldap' ? profileEmail : generatedEmail; var username = provider + '.' + (profile.username || profile.id); password = utils.generateKey('password'); userObj = { username: username, password: password, }; if (email) { userObj.email = email; } return userObj; } else { password = utils.generateKey('password'); userObj = { password: password, }; // if profileMapping exists, map each provider field to the appropriate user field options.profileMapping.forEach(function(field) { userObj[field.userField] = stringToRef(profile, field.providerField); }); } return userObj; } If you replace it with a function that looks like this:

function profileToUser(provider, profile, options) {
  return {
    firstName: profile.name.givenName,
    lastName: profile.name.familyName,
    email: profile.email,
  }
}

The passport component will properly match the player with its email and not duplicate the account.

this is not working fine, suggest something else to do this manually when new user registered.

shubhddn01 commented 5 years ago

Looking for the solution... :(

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] commented 4 years ago

This issue has been closed due to continued inactivity. Thank you for your understanding. If you believe this to be in error, please contact one of the code owners, listed in the CODEOWNERS file at the top-level of this repository.