Linking multiple providers to an account

This document shows you how to link multiple providers to a single Identity Platform account.

Identity Platform uses a unique ID to identify users. This allows users to sign in to the same account with different providers. For example, a user who initially registered with a phone number could later link their Google account, and then use either method to sign in.

Before you begin

Add support for two or more identity providers to your app.

Enabling or disabling account linking

The account linking setting determines how Identity Platform handles users attempting to sign in with the same email using different providers.

  • Link accounts that use the same email: Identity Platform will raise an error if a user tries to sign in with an email that's already in use. Your app can catch this error, and link the new provider to their existing account.

  • Create multiple accounts for each identity provider: A new Identity Platform user account will be created each time a user signs in with a different provider.

To choose a setting:

  1. Go to the Identity Platform Settings page in the Cloud Console.

    Go to the Settings page

  2. Select a setting under User account linking.

  3. Click Save.

Linking federated provider credentials

To link credentials from a federated provider:

  1. Sign in the user with any authentication provider or method.

  2. Get the provider object that corresponds to the provider you want to link to the user's account. For example:

    JavaScript

    var googleProvider = new firebase.auth.GoogleAuthProvider();
    var facebookProvider = new firebase.auth.FacebookAuthProvider();
    var twitterProvider = new firebase.auth.TwitterAuthProvider();
    var githubProvider = new firebase.auth.GithubAuthProvider();
  3. Prompt the user to sign in with the provider you want to link. You can either open a pop-up window, or redirect the current page. Redirecting is easier for users on mobile devices.

    To show a pop-up, call linkWithPopup():

    JavaScript

    auth.currentUser.linkWithPopup(provider).then(function(result) {
      // Accounts successfully linked.
      var credential = result.credential;
      var user = result.user;
      // ...
    }).catch(function(error) {
      // Handle Errors here.
      // ...
    });

    To redirect the page, first call linkWithRedirect():

    JavaScript

    auth.currentUser.linkWithRedirect(provider)
      .then(/* ... */)
      .catch(/* ... */);

    After the user signs in, they'll be redirected back to your app. Then, you can retrieve the sign-in result by calling getRedirectResult():

    JavaScript

    auth.getRedirectResult().then(function(result) {
      if (result.credential) {
        // Accounts successfully linked.
        var credential = result.credential;
        var user = result.user;
        // ...
      }
    }).catch(function(error) {
      // Handle Errors here.
      // ...
    });

The user's account with the federated provider is now linked to their Identity Platform account, and they can use the provider to sign in.

Linking email and password credentials

To add an email address and password to an existing user account:

  1. Sign in the user with any identity provider or method.

  2. Prompt the user for an email address and password.

  3. Create an AuthCredential object with the email address and password:

    JavaScript

    var credential = firebase.auth.EmailAuthProvider.credential(email, password);
  4. Pass the AuthCredential object to the linkWithCredential() method on the signed-in user:

    JavaScript

    auth.currentUser.linkWithCredential(credential)
      .then(function(usercred) {
        var user = usercred.user;
        console.log("Account linking success", user);
      }).catch(function(error) {
        console.log("Account linking error", error);
      });

The email and password credentials are now linked to the user's Identity Platform account, and they can use them to sign in.

Handling the account-exists-with-different-credential error

If you've enabled the Link accounts that use the same email setting, Identity Platform will raise an error when a user tries to sign in with a provider using an email that's already associated with an existing account.

To handle this error, prompt the user to sign in with an existing account. Then call linkWithCredential(), linkWithPopup(), or linkWithRedirect() to associate the new provider to their account.

The following example shows how to handle this error when a user attempts to sign in using Facebook:

JavaScript

// User tries to sign in with Facebook.
auth.signInWithPopup(new firebase.auth.FacebookAuthProvider()).catch(err => {
  // User's email already exists.
  if (err.code === 'auth/account-exists-with-different-credential') {
    // The pending Facebook credential.
    var pendingCred = err.credential;
    // The provider account's email address.
    var email = err.email;
    // Get the sign-in methods for this email.
    auth.fetchSignInMethodsForEmail(email).then(methods => {
      // If the user has several sign-in methods, the first method
      // in the list will be the "recommended" method to use.
      if (methods[0] === 'password') {
        // TODO: Ask the user for their password.
        // In real scenario, you should handle this asynchronously.
        var password = promptUserForPassword();
        auth.signInWithEmailAndPassword(email, password).then(result => {
          return result.user.linkWithCredential(pendingCred);
        }).then(() => {
          // Facebook account successfully linked to the existing user.
          goToApp();
        });
        return;
      }
      // All other cases are external providers.
      // Construct provider object for that provider.
      // TODO: Implement getProviderForProviderId.
      var provider = getProviderForProviderId(methods[0]);
      // At this point, you should let the user know that they already have an
      // account with a different provider, and validate they want to sign in
      // with the new provider.
      // Note: Browsers usually block popups triggered asynchronously, so in
      // real app, you should ask the user to click on a "Continue" button
      // that will trigger signInWithPopup().
      auth.signInWithPopup(provider).then(result => {
        // Note: Identity Platform doesn't control the provider's sign-in
        // flow, so it's possible for the user to sign in with an account
        // with a different email from the first one.

        // Link the Facebook credential. We have access to the pending
        // credential, so we can directly call the link method.
        result.user.linkWithCredential(pendingCred).then(usercred => {
          // Success.
          goToApp();
        });
      });
    });
  }
});

Using a redirect is similar to a popup, except you'll need to cache the pending credential between page redirects (for example, by using session storage).

Note that some providers, such as Google and Microsoft, serve as both email and social identity providers. Email providers are considered authoritative for all addresses related to their hosted email domain. This means a user logging in with an email address hosted by the same provider will never raise this error (for example, signing in with Google using an @gmail.com email, or Microsoft using an @live.com or @outlook.com email).

Merging accounts manually

If a user attempts to sign in with credentials that are already linked to another user account using the same provider, the Client SDK's built-in methods for account linking will fail. In this situation, you'll need to merge the accounts manually, and then delete the second account. For example:

JavaScript

// Sign in first account.
const result1 = await auth.signInWithCredential(cred1);
const user1 = result1.user;
// Try to link a credential that belongs to an existing account
try {
  await user1.linkWithCredential(cred2);
} catch (error) {
  // cred2 already exists so an error is thrown.
  const result2 = await auth.signInWithCredential(error.credential);
  const user2 = result2.user;
  // Merge the data.
  mergeData(user1, user2);
  // Delete one of the accounts, and try again.
  await user2.delete();
  // Linking now will work.
  await user1.linkWithCredential(result2.credential);
}

You can unlink a provider from a user's account. The user will no longer be able to authenticate with that provider.

To unlink a provider, pass the provider ID to the unlink() method on the user. You can get the ID from the providerData property.

JavaScript

user.unlink(providerId).then(function() {
  // Auth provider unlinked from account
  // ...
}).catch(function(error) {
  // An error happened
  // ...
});

What's next