Firebase-js-sdk: Inconsistency with 'auth/account-exists-with-different-credential'

Created on 7 Nov 2017  路  7Comments  路  Source: firebase/firebase-js-sdk

[REQUIRED] Describe your environment

  • Operating System version: Mac OS High Sierra
  • Firebase SDK version: 4.6.1
  • Firebase Product: auth

[REQUIRED] Describe the problem

We're seeing weird behavior with getRedirectResult when the error "auth/account-exists-with-different-credential" gets thrown.

Basically our app supports Google and Facebook logins. It's a mobile app, so we use signInWithRedirect. When the redirect flow is done, we call getRedirectResult, and this is where it gets inconsistent:

If a new users logs in via Google _first_, and then Facebook, getRedirectResult throws the above error, which we can handle to link the two accounts.

However, if a new user logs in via Facebook _first_, and then Google, getRedirectResult does NOT throw the above error, and the Facebook provider in Firebase is just replaced with the Google one.

So thats kinda of inconsistent in our eyes.

Is this intentional behavior?

auth

All 7 comments

Hmmm this issue does not seem to follow the issue template. Make sure you provide all the required information.

There are a lot of examples on how to handle these errors and why this doesn't happen with Google provider on stackoverflow.

Here is an example how to recover from the above error:

var facebookCred = null;
firebase.auth().getRedirectResult()
  .catch(function(error) {
    if (error.code == 'auth/account-exists-with-different-credential') {
       // Save Facebook credential.
       facebookCred = error.credential;
       // If you already know there is an existing Google account.
       var googleProvider = new firebase.auth.GoogleAuthProvider();
       // Login to the existing Google account with the same email.
       googleProvider.customOAuthParameters({'login_hint': error.email});
       return firebase.auth().signInWithPopup(googleProvider)
          .then(function(result) {
            // Link Facebook credential to Google account.
            return result.user.linkWithCredential(facebookCred);
          });
    }
    throw error;
  });

So this happens when you first sign in with Google and then Facebook or Email/Password because Google is the issuer of the email and the emailVerified is true when you sign in with Google. Since after you sign in with Facebook and the emails match, we can't confirm Facebook is the owner of the account, so you need to prove ownership of the original Google account before you link the Facebook credential.

On the other hand when you sign in with Facebook and then sign in with Google, the Facebook account will be overwritten since the Google account is already verified whereas the Facebook account is not. You don't need to provide ownership in that case (Google email accounts are issued by Google).

Check this post for more on this:
https://groups.google.com/forum/#!searchin/firebase-talk/liu/firebase-talk/ms_NVQem_Cw/8g7BFk1IAAAJ

@bojeil-google Thanks for the reply.

We did see the various questions on SO, but we convinced ourselves that this could not be intended behavior.

Lemme explain a bit about our use case
Our system is based on shared terminals, and when a user logs out, we purge the device of all data related to that user, and we log out the user from facebook and google.

So when a user logs in, the system knows absolutely nothing about that user, because the device is effectively empty.

So we figured we could handle it like this (psudo-code):

// (The facebook login is basically identical)
async loginGoogle(email?: string) {
    // Get the provider
    const googleProvider = this.getGoogleProvider(email);
    // Sign in, this opens cordova.browsertab and all that stuff
    await this.auth.auth.signInWithRedirect(googleProvider);
    // Here is where we'd expect a collision if the user was already registered, but due to the reasons you explained, that obviously doesn't happen. So the accounts never get linked unless the users logs out again and in with Facebook.
    return this.auth.auth.getRedirectResult()
      .catch(this.linkIfDuplicateAccount);
  }

It will still work, but for accounts to be linked for a Facebook -> Google login flow, it effectively becomes Facebook -> Google -> Facebook, which is a bit annoying for the end user.

We're not gonna ask our users to link accounts while logged in, as our users wouldn't understand that. They just want to press a login button. 馃槃

In the flow:
Facebook -> Google
where they previously signed up with Facebook and then signed in with Google,.
In that case, Facebook gets unlinked, you don't have to link Facebook after Google auth succeeds. You can, the next time they login with Facebook and the error is thrown, sign in with Google and then link the Facebook credential.

BTW, you always have the choice to switch to multiple accounts per email where you would create a new user for each provider even if they match emails. An error is not thrown when the email already exists. In that case, you can handle account merging on your own.

You can, the next time they login with Facebook and the error is thrown, sign in with Google and then link the Facebook credential.

And thats how its working now. But effectively, thats Facebook -> Google -> Facebook, which is what we were hoping to avoid.

We thought about multiple accounts, but that creates its own challenges in our particular system.
We can live with the 3 step flow for now, its just a bit iffy from a user perspective. So we'll stick with it for now, as our options seem limited.

At least now you know our particular use case.

I guess I'll close this, as it is intended behavior. Could you ask the documentation people to document this? There are various places that really should mention this, like https://firebase.google.com/docs/reference/js/firebase.auth.Auth#getRedirectResult

Thanks for listening to our lamentations :-)

We provide documentation on how to handle auth/account-exists-with-different-credential errors: https://firebase.google.com/docs/auth/web/google-signin#handling-account-exists-with-different-credential-errors
But I agree we should provide more information on verified email provider behavior. Thanks for the feedback.

Hi, curious what the reasoning behind this is? I've implemented oauth before and linking to a single account by email is trivial as I'm sure you know, and could have been done automatically if the one-account-per-email was chosen (which is pretty much everyone's
choice I'm guessing).

I think a user should be able to choose whatever social account (with the same email) they want right? The inverse would be the edge case that requires more code?

Was this page helpful?
0 / 5 - 0 ratings