Firebase lets users link multiple sign-in methods to a single account using linkWithPopup, linkWithCredential, or linkWithPhoneNumber. This means a user who signed up with email can later add Google or phone sign-in. The key challenge is handling the auth/account-exists-with-different-credential error, which fires when a user tries to sign in with a provider that uses an email already registered under a different provider. Use fetchSignInMethodsForEmail to detect this and guide the user through linking.
Linking Multiple Auth Providers to One Firebase Account
Users expect to sign in with any method and reach the same account. Firebase supports linking multiple auth providers (Google, GitHub, email, phone) to a single user record. This tutorial covers the linking flow, the most common error scenario when two providers share the same email, and how to build a UI that shows linked providers and lets users manage them.
Prerequisites
- A Firebase project with at least two auth providers enabled
- Firebase JS SDK v9+ installed in your project
- A signed-in user to link additional providers to
- Basic understanding of Firebase Auth and OAuth flows
Step-by-step guide
Check which providers are already linked
Check which providers are already linked
Before linking a new provider, inspect the current user's providerData array. Each entry represents a linked provider and includes the providerId (e.g. 'google.com', 'password', 'phone'), the email or phone number, and the display name. This lets you show which methods are active and prevent trying to link a provider that is already connected.
1import { getAuth } from 'firebase/auth'23const auth = getAuth()45function getLinkedProviders() {6 const user = auth.currentUser7 if (!user) return []89 return user.providerData.map((provider) => ({10 id: provider.providerId,11 email: provider.email,12 phone: provider.phoneNumber,13 name: provider.displayName,14 }))15}1617// Example output:18// [{ id: 'password', email: 'user@example.com', ... },19// { id: 'google.com', email: 'user@gmail.com', ... }]Expected result: An array of linked providers for the current user, which you can use to render toggle buttons in your settings UI.
Link an OAuth provider with linkWithPopup
Link an OAuth provider with linkWithPopup
To add Google, GitHub, or another OAuth provider to an existing account, create a provider instance and call linkWithPopup on the current user. This opens the OAuth consent screen. On success, the provider is permanently linked and the user can sign in with either method. If the provider's email is already used by a different Firebase account, this will throw auth/credential-already-in-use.
1import { GoogleAuthProvider, linkWithPopup } from 'firebase/auth'23async function linkGoogle() {4 const user = auth.currentUser5 if (!user) throw new Error('Must be signed in')67 const provider = new GoogleAuthProvider()89 try {10 const result = await linkWithPopup(user, provider)11 console.log('Google linked:', result.user.providerData)12 } catch (error: any) {13 if (error.code === 'auth/credential-already-in-use') {14 console.error('This Google account is already linked to another user.')15 } else if (error.code === 'auth/provider-already-linked') {16 console.error('Google is already linked to this account.')17 }18 throw error19 }20}Expected result: The Google provider appears in the user's providerData array and the user can sign in with Google or their original method.
Handle account-exists-with-different-credential
Handle account-exists-with-different-credential
When a user tries to sign in with a new provider (e.g. GitHub) but the email is already registered under a different provider (e.g. Google), Firebase throws auth/account-exists-with-different-credential. To resolve this, use fetchSignInMethodsForEmail to find the existing provider, sign the user in with that provider, then link the new credential to merge both providers into one account.
1import {2 signInWithPopup,3 GoogleAuthProvider,4 GithubAuthProvider,5 fetchSignInMethodsForEmail,6 linkWithCredential,7 OAuthProvider,8} from 'firebase/auth'910async function signInWithGitHub() {11 const provider = new GithubAuthProvider()1213 try {14 const result = await signInWithPopup(auth, provider)15 return result.user16 } catch (error: any) {17 if (error.code === 'auth/account-exists-with-different-credential') {18 const email = error.customData?.email19 const pendingCred = OAuthProvider.credentialFromError(error)20 if (!email || !pendingCred) throw error2122 // Find which provider the email is registered with23 const methods = await fetchSignInMethodsForEmail(auth, email)24 console.log('Sign in with', methods[0], 'first, then link GitHub')2526 // Sign in with the existing provider (e.g. Google)27 const googleProvider = new GoogleAuthProvider()28 const googleResult = await signInWithPopup(auth, googleProvider)2930 // Link the pending GitHub credential31 await linkWithCredential(googleResult.user, pendingCred)32 console.log('GitHub linked successfully')33 return googleResult.user34 }35 throw error36 }37}Expected result: Both providers are linked to the same account and the user can sign in with either Google or GitHub in the future.
Link email/password credentials to an OAuth account
Link email/password credentials to an OAuth account
If a user originally signed in with Google and wants to add a password for direct email login, use EmailAuthProvider.credential() and linkWithCredential(). This is useful for users who want a fallback sign-in method or for apps that require a password for sensitive actions.
1import { EmailAuthProvider, linkWithCredential } from 'firebase/auth'23async function linkEmailPassword(4 email: string,5 password: string6) {7 const user = auth.currentUser8 if (!user) throw new Error('Must be signed in')910 const credential = EmailAuthProvider.credential(email, password)1112 try {13 const result = await linkWithCredential(user, credential)14 console.log('Email/password linked:', result.user.email)15 } catch (error: any) {16 if (error.code === 'auth/email-already-in-use') {17 console.error('This email is already used by another account.')18 } else if (error.code === 'auth/weak-password') {19 console.error('Password must be at least 6 characters.')20 }21 throw error22 }23}Expected result: The email/password provider is linked and the user can now sign in with either Google or email/password.
Unlink a provider from the account
Unlink a provider from the account
Users may want to remove a linked provider. Call unlink() on the current user with the providerId to remove. Firebase prevents unlinking the last remaining provider — the user must always have at least one sign-in method. Check providerData.length before unlinking to warn the user.
1import { unlink } from 'firebase/auth'23async function unlinkProvider(providerId: string) {4 const user = auth.currentUser5 if (!user) throw new Error('Must be signed in')67 if (user.providerData.length <= 1) {8 throw new Error('Cannot unlink the only sign-in method')9 }1011 try {12 const updatedUser = await unlink(user, providerId)13 console.log('Unlinked', providerId)14 console.log('Remaining providers:', updatedUser.providerData.map(p => p.providerId))15 } catch (error: any) {16 if (error.code === 'auth/no-such-provider') {17 console.error('This provider is not linked to this account.')18 }19 throw error20 }21}Expected result: The specified provider is removed from the user's account. The user can no longer sign in with that method.
Complete working example
1import { initializeApp } from 'firebase/app'2import {3 getAuth,4 GoogleAuthProvider,5 GithubAuthProvider,6 EmailAuthProvider,7 linkWithPopup,8 linkWithCredential,9 unlink,10 fetchSignInMethodsForEmail,11 OAuthProvider,12 signInWithPopup,13 onAuthStateChanged,14} from 'firebase/auth'1516const app = initializeApp({17 apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY!,18 authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN!,19 projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID!,20})2122const auth = getAuth(app)2324export function getLinkedProviders() {25 return auth.currentUser?.providerData.map((p) => p.providerId) ?? []26}2728export async function linkGoogle() {29 if (!auth.currentUser) throw new Error('Sign in first')30 return linkWithPopup(auth.currentUser, new GoogleAuthProvider())31}3233export async function linkGitHub() {34 if (!auth.currentUser) throw new Error('Sign in first')35 return linkWithPopup(auth.currentUser, new GithubAuthProvider())36}3738export async function linkEmail(email: string, password: string) {39 if (!auth.currentUser) throw new Error('Sign in first')40 const cred = EmailAuthProvider.credential(email, password)41 return linkWithCredential(auth.currentUser, cred)42}4344export async function unlinkProvider(providerId: string) {45 if (!auth.currentUser) throw new Error('Sign in first')46 if (auth.currentUser.providerData.length <= 1) {47 throw new Error('Cannot remove last provider')48 }49 return unlink(auth.currentUser, providerId)50}5152export function onAuth(cb: (user: any) => void) {53 return onAuthStateChanged(auth, cb)54}Common mistakes when linkking Multiple Auth Providers in Firebase
Why it's a problem: Trying to link a provider that is already linked, throwing auth/provider-already-linked
How to avoid: Check user.providerData for the providerId before calling linkWithPopup or linkWithCredential.
Why it's a problem: Losing the pending credential during the account-exists-with-different-credential flow when using redirect auth
How to avoid: Store the pending credential in sessionStorage before redirecting to the existing provider's sign-in. Retrieve it after the redirect completes.
Why it's a problem: Allowing the user to unlink their only sign-in method, which locks them out
How to avoid: Check providerData.length before unlinking and disable the unlink button when only one provider remains.
Best practices
- Always check providerData before linking to avoid auth/provider-already-linked errors
- Handle auth/account-exists-with-different-credential by guiding the user to sign in with their existing provider first
- Store pending credentials in sessionStorage during redirect flows to prevent data loss
- Show users a clear list of linked providers in their account settings
- Prevent unlinking the last remaining provider to avoid locking users out
- Use linkWithPopup on desktop and linkWithRedirect on mobile for the best UX
- After linking or unlinking, call user.reload() to refresh the local user object
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I have a Firebase app where users can sign in with Google, GitHub, or email/password. Show me how to link multiple auth providers to one account using the modular SDK v9. Include handling for the account-exists-with-different-credential error and a way to unlink providers.
Add an account settings panel that shows which auth providers are linked (Google, GitHub, email, phone). Let users link new providers with linkWithPopup and unlink existing ones. Handle the account-exists-with-different-credential error gracefully.
Frequently asked questions
How many auth providers can I link to one Firebase account?
There is no hard limit on the number of providers you can link to a single account. You can link email/password, phone, and multiple OAuth providers (Google, GitHub, Facebook, Apple, etc.) to the same user.
What happens if two users have the same email with different providers?
By default, Firebase blocks sign-in with auth/account-exists-with-different-credential. You should handle this by prompting the user to sign in with their existing provider and then linking the new provider.
Can I link a phone number to an email/password account?
Yes. Use linkWithPhoneNumber on the current user to add phone auth as an additional sign-in method. The Blaze plan is required for phone auth.
Does linking providers merge user data?
No. Linking only merges authentication methods. If the user had data under a separate UID from a different account, you need to manually merge that data in your database.
Can I automatically link accounts with the same email?
Firebase does not automatically link accounts. You must handle the account-exists-with-different-credential error and guide the user through the linking flow. This is a deliberate security measure.
Can RapidDev help implement multi-provider auth linking in my app?
Yes. RapidDev can build a complete multi-provider authentication system with account linking, conflict resolution, and a settings UI for managing connected providers.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation