# How to Link Multiple Auth Providers in Firebase

- Tool: Firebase
- Difficulty: Intermediate
- Time required: 15-20 min
- Compatibility: Firebase JS SDK v9+, all plans
- Last updated: March 2026

## TL;DR

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.

## Before you start

- 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

### 1. 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.

```
import { getAuth } from 'firebase/auth'

const auth = getAuth()

function getLinkedProviders() {
  const user = auth.currentUser
  if (!user) return []

  return user.providerData.map((provider) => ({
    id: provider.providerId,
    email: provider.email,
    phone: provider.phoneNumber,
    name: provider.displayName,
  }))
}

// Example output:
// [{ id: 'password', email: 'user@example.com', ... },
//  { 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.

### 2. 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.

```
import { GoogleAuthProvider, linkWithPopup } from 'firebase/auth'

async function linkGoogle() {
  const user = auth.currentUser
  if (!user) throw new Error('Must be signed in')

  const provider = new GoogleAuthProvider()

  try {
    const result = await linkWithPopup(user, provider)
    console.log('Google linked:', result.user.providerData)
  } catch (error: any) {
    if (error.code === 'auth/credential-already-in-use') {
      console.error('This Google account is already linked to another user.')
    } else if (error.code === 'auth/provider-already-linked') {
      console.error('Google is already linked to this account.')
    }
    throw error
  }
}
```

**Expected result:** The Google provider appears in the user's providerData array and the user can sign in with Google or their original method.

### 3. 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.

```
import {
  signInWithPopup,
  GoogleAuthProvider,
  GithubAuthProvider,
  fetchSignInMethodsForEmail,
  linkWithCredential,
  OAuthProvider,
} from 'firebase/auth'

async function signInWithGitHub() {
  const provider = new GithubAuthProvider()

  try {
    const result = await signInWithPopup(auth, provider)
    return result.user
  } catch (error: any) {
    if (error.code === 'auth/account-exists-with-different-credential') {
      const email = error.customData?.email
      const pendingCred = OAuthProvider.credentialFromError(error)
      if (!email || !pendingCred) throw error

      // Find which provider the email is registered with
      const methods = await fetchSignInMethodsForEmail(auth, email)
      console.log('Sign in with', methods[0], 'first, then link GitHub')

      // Sign in with the existing provider (e.g. Google)
      const googleProvider = new GoogleAuthProvider()
      const googleResult = await signInWithPopup(auth, googleProvider)

      // Link the pending GitHub credential
      await linkWithCredential(googleResult.user, pendingCred)
      console.log('GitHub linked successfully')
      return googleResult.user
    }
    throw error
  }
}
```

**Expected result:** Both providers are linked to the same account and the user can sign in with either Google or GitHub in the future.

### 4. 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.

```
import { EmailAuthProvider, linkWithCredential } from 'firebase/auth'

async function linkEmailPassword(
  email: string,
  password: string
) {
  const user = auth.currentUser
  if (!user) throw new Error('Must be signed in')

  const credential = EmailAuthProvider.credential(email, password)

  try {
    const result = await linkWithCredential(user, credential)
    console.log('Email/password linked:', result.user.email)
  } catch (error: any) {
    if (error.code === 'auth/email-already-in-use') {
      console.error('This email is already used by another account.')
    } else if (error.code === 'auth/weak-password') {
      console.error('Password must be at least 6 characters.')
    }
    throw error
  }
}
```

**Expected result:** The email/password provider is linked and the user can now sign in with either Google or email/password.

### 5. 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.

```
import { unlink } from 'firebase/auth'

async function unlinkProvider(providerId: string) {
  const user = auth.currentUser
  if (!user) throw new Error('Must be signed in')

  if (user.providerData.length <= 1) {
    throw new Error('Cannot unlink the only sign-in method')
  }

  try {
    const updatedUser = await unlink(user, providerId)
    console.log('Unlinked', providerId)
    console.log('Remaining providers:', updatedUser.providerData.map(p => p.providerId))
  } catch (error: any) {
    if (error.code === 'auth/no-such-provider') {
      console.error('This provider is not linked to this account.')
    }
    throw error
  }
}
```

**Expected result:** The specified provider is removed from the user's account. The user can no longer sign in with that method.

## Complete code example

File: `auth-provider-linking.ts`

```typescript
import { initializeApp } from 'firebase/app'
import {
  getAuth,
  GoogleAuthProvider,
  GithubAuthProvider,
  EmailAuthProvider,
  linkWithPopup,
  linkWithCredential,
  unlink,
  fetchSignInMethodsForEmail,
  OAuthProvider,
  signInWithPopup,
  onAuthStateChanged,
} from 'firebase/auth'

const app = initializeApp({
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY!,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN!,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID!,
})

const auth = getAuth(app)

export function getLinkedProviders() {
  return auth.currentUser?.providerData.map((p) => p.providerId) ?? []
}

export async function linkGoogle() {
  if (!auth.currentUser) throw new Error('Sign in first')
  return linkWithPopup(auth.currentUser, new GoogleAuthProvider())
}

export async function linkGitHub() {
  if (!auth.currentUser) throw new Error('Sign in first')
  return linkWithPopup(auth.currentUser, new GithubAuthProvider())
}

export async function linkEmail(email: string, password: string) {
  if (!auth.currentUser) throw new Error('Sign in first')
  const cred = EmailAuthProvider.credential(email, password)
  return linkWithCredential(auth.currentUser, cred)
}

export async function unlinkProvider(providerId: string) {
  if (!auth.currentUser) throw new Error('Sign in first')
  if (auth.currentUser.providerData.length <= 1) {
    throw new Error('Cannot remove last provider')
  }
  return unlink(auth.currentUser, providerId)
}

export function onAuth(cb: (user: any) => void) {
  return onAuthStateChanged(auth, cb)
}
```

## Common mistakes

- **Trying to link a provider that is already linked, throwing auth/provider-already-linked** — undefined Fix: Check user.providerData for the providerId before calling linkWithPopup or linkWithCredential.
- **Losing the pending credential during the account-exists-with-different-credential flow when using redirect auth** — undefined Fix: Store the pending credential in sessionStorage before redirecting to the existing provider's sign-in. Retrieve it after the redirect completes.
- **Allowing the user to unlink their only sign-in method, which locks them out** — undefined Fix: 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

## 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.

---

Source: https://www.rapidevelopers.com/firebase-tutorial/how-to-link-multiple-auth-providers-in-firebase
© RapidDev — https://www.rapidevelopers.com/firebase-tutorial/how-to-link-multiple-auth-providers-in-firebase
