How to allow signing-up through multiple authentication-providers, while sharing the same email-address?

StackOverflow https://stackoverflow.com/questions/23388965

  •  12-07-2023
  •  | 
  •  

Question

This is probably a question a bit in between SO and UX.se.

I'm implementing a signup/login sytem which allows for multiple ways of authentication: social signups using Facebook, Twitter, Github and a local (username + password) signup.

In the backend I'm having a User model and a Passport model. A User may have multiple Passports, meaning a User may authenticate through different means (e.g.: through local signin,. or through facebook for example)

For good measure: the Passports of a particular User will always be from a different provider (facebook, twitter, local). I.e.: a Facebook-passport, a Local-passport, etc.

This seems like a good way and would allow me to have a User account connected to multiple ways of authentication.

I'm familiar with the security issues this might raise, so for passports to be combined/merged a User has to be logged in to both.

Ok on to the problem. Consider the following flow:

  1. user-a signs up with a provider, say local, with email user-a@gmail.com
  2. user-a signs out (or has it's session expired).
  3. user-a signs in using another provider, say facebook. Chances are the facebook account has a email-record of user-a@gmail.com

Currently, I've defined email to be unique on the User-model. This would mean the above signup would fail, because there's already a User-account that, by means of the local Passport, has the mentioned email-address.

What would be considered a best practice in a situation like this? I trust there must be many implementations floating around that must have seen this problem pop-up.

Options:

  1. Warn the user the authentication isn't possible and mention the user that the current email-address is already registered by means of another authentication mechanism? This would be reasonably user-friendly.
  2. Note that a User-account, by means of a different provider exists with the same email-address and as a consequence merge the new Passport with the User.. I just put this in for good measure: this is a pretty big attack-vector and would allow a user-b to get access to an account by faking the email-address (through a social provider which doesn't do email-validation)
  3. Don't have a uniqueness constraint on <user,email>, but on <passport, email>. This would allow a new User and associated Passport to be created and all goes well. Now the same actual person probably has 2 User Accounts: 1 for each authentication provider. As a next stage allow the User-accounts to be merged, by signing in to both and acknowledging the merge.

I'm leaning towards 3, but 1 is simpler. I'm not sure, what do you think? Have you encountered this before?

Was it helpful?

Solution

Yes, #3. I have implemented this myself. The concept you're looking at is: Associate multiple SSO accounts. My user structure is as follows:

name : { 
    first:  { type: String},
    last:   { type: String }
},
emails: [{ type: String, unique: true, 'index': true }],  //all known emails as provided by SSO services. we use this to cross ref when the user uses a different SSO to login after initial setup. this avoids account dupes
sso: [{
    provider:   { type: String, required: true}, //matches the name of passport strategy name employed
    userid:     { type: String, required: true } //the specific SSO provider userID that's unique in the provider's realm
}]

So, in your auth sequence, you look it up by email OR provider+userid combo, if you don't find the SSO provider, you attach it. The reason for or, someone may update their email but the specific SSO provider ID never changes.

Another common practice (if it makes sense in your app) is to allow the user to "link" SSO accounts. That allows you to handle different email addresses. Example: user FB email is a personal one but in LinkedIn he lists as primary the business one. LinkedIn sadly gives you only the primary via their OAuth2 call.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top