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:
Go to the Identity Platform Settings page in the Google Cloud console.
Select a setting under User account linking.
Click Save.
Linking federated provider credentials
To link credentials from a federated provider:
Sign in the user with any authentication provider or method.
Get the provider object that corresponds to the provider you want to link to the user's account. For example:
Web version 9
import { GoogleAuthProvider, FacebookAuthProvider, TwitterAuthProvider, GithubAuthProvider } from "firebase/auth"; const googleProvider = new GoogleAuthProvider(); const facebookProvider = new FacebookAuthProvider(); const twitterProvider = new TwitterAuthProvider(); const githubProvider = new GithubAuthProvider();
Web version 8
var googleProvider = new firebase.auth.GoogleAuthProvider(); var facebookProvider = new firebase.auth.FacebookAuthProvider(); var twitterProvider = new firebase.auth.TwitterAuthProvider(); var githubProvider = new firebase.auth.GithubAuthProvider();
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()
:Web version 9
import { getAuth, linkWithPopup, GoogleAuthProvider } from "firebase/auth"; const provider = new GoogleAuthProvider(); const auth = getAuth(); linkWithPopup(auth.currentUser, provider).then((result) => { // Accounts successfully linked. const credential = GoogleAuthProvider.credentialFromResult(result); const user = result.user; // ... }).catch((error) => { // Handle Errors here. // ... });
Web version 8
auth.currentUser.linkWithPopup(provider).then((result) => { // Accounts successfully linked. var credential = result.credential; var user = result.user; // ... }).catch((error) => { // Handle Errors here. // ... });
To redirect the page, first call
linkWithRedirect()
:Follow the best practices when using
signInWithRedirect
,linkWithRedirect
, orreauthenticateWithRedirect
.Web version 9
import { getAuth, linkWithRedirect, GoogleAuthProvider } from "firebase/auth"; const provider = new GoogleAuthProvider(); const auth = getAuth(); linkWithRedirect(auth.currentUser, provider) .then(/* ... */) .catch(/* ... */);
Web version 8
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()
:Web version 9
import { getRedirectResult } from "firebase/auth"; getRedirectResult(auth).then((result) => { const credential = GoogleAuthProvider.credentialFromResult(result); if (credential) { // Accounts successfully linked. const user = result.user; // ... } }).catch((error) => { // Handle Errors here. // ... });
Web version 8
auth.getRedirectResult().then((result) => { if (result.credential) { // Accounts successfully linked. var credential = result.credential; var user = result.user; // ... } }).catch((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:
Sign in the user with any identity provider or method.
Prompt the user for an email address and password.
Create an
AuthCredential
object with the email address and password:Web version 9
import { EmailAuthProvider } from "firebase/auth"; const credential = EmailAuthProvider.credential(email, password);
Web version 8
var credential = firebase.auth.EmailAuthProvider.credential(email, password);
Pass the
AuthCredential
object to thelinkWithCredential()
method on the signed-in user:Web version 9
import { getAuth, linkWithCredential } from "firebase/auth"; const auth = getAuth(); linkWithCredential(auth.currentUser, credential) .then((usercred) => { const user = usercred.user; console.log("Account linking success", user); }).catch((error) => { console.log("Account linking error", error); });
Web version 8
auth.currentUser.linkWithCredential(credential) .then((usercred) => { var user = usercred.user; console.log("Account linking success", user); }).catch((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.
Note that a federated provider credential can be linked to an email/password account with a different email. If this happens, the email corresponding to the federated provider can be used to create a separate email/password account.
Handling the account-exists-with-different-credential error
If you enabled the Link accounts that use the same email setting in
Google Cloud console, when a user tries to sign in to a provider
(such as SAML) with an email that already exists for another provider (such as
Google), the error auth/account-exists-with-different-credential
is thrown
(along with an AuthCredential
object).
To handle this error, prompt the user to sign in with the existing provider.
Then call linkWithCredential()
, linkWithPopup()
, or
linkWithRedirect()
to associate the new provider to their account using the AuthCredential
.
The following example shows how to handle this error when a user attempts to sign in using Facebook:
Web version 9
import { signInWithPopup, signInWithEmailAndPassword, linkWithCredential } from "firebase/auth"; // User tries to sign in with Facebook. signInWithPopup(auth, facebookProvider).catch((error) => { // User's email already exists. if (error.code === 'auth/account-exists-with-different-credential') { // The pending Facebook credential. const pendingCred = error.credential; // The provider account's email address. const email = error.customData.email; // Present the user with a list of providers they might have // used to create the original account. // Then, ask the user to sign in with the existing provider. const method = promptUserForSignInMethod(); if (method === 'password') { // TODO: Ask the user for their password. // In real scenario, you should handle this asynchronously. const password = promptUserForPassword(); signInWithEmailAndPassword(auth, email, password).then((result) => { return linkWithCredential(result.user, 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. const provider = getProviderForProviderId(method); // 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(). signInWithPopup(auth, 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. linkWithCredential(result.user, pendingCred).then((userCred) => { // Success. goToApp(); }); }); } });
Web version 8
// User tries to sign in with Facebook. auth.signInWithPopup(facebookProvider).catch((error) => { // User's email already exists. if (error.code === 'auth/account-exists-with-different-credential') { // The pending Facebook credential. const pendingCred = error.credential; // The provider account's email address. const email = error.email; // Present the user with a list of providers they might have // used to create the original account. // Then, ask the user to sign in with the existing provider. const method = promptUserForSignInMethod(); if (method === 'password') { // TODO: Ask the user for their password. // In real scenario, you should handle this asynchronously. const 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. const provider = getProviderForProviderId(method); // 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:
Web version 9
// Sign in first account.
const result1 = await signInWithCredential(auth, cred1);
const user1 = result1.user;
// Try to link a credential that belongs to an existing account
try {
await linkWithCredential(user1, cred2);
} catch (error) {
// cred2 already exists so an error is thrown.
const result2 = await signInWithCredential(auth, 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 linkWithCredential(user1, result2.credential);
}
Web version 8
// 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);
}
Unlinking a provider
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.
You can get the provider IDs of the auth providers linked to a user from the providerData
property.
Web version 9
import { getAuth, unlink } from "firebase/auth"; const auth = getAuth(); unlink(auth.currentUser, providerId).then(() => { // Auth provider unlinked from account // ... }).catch((error) => { // An error happened // ... });
Web version 8
user.unlink(providerId).then(() => { // Auth provider unlinked from account // ... }).catch((error) => { // An error happened // ... });
What's next
- Add support for different identity providers to your app.
- Learn how to manage providers programmatically.