# What is a good Firebase Auth alternative?

> Good Firebase Auth alternatives are Supabase Auth for Postgres apps with row-level security, Clerk or Auth0 for hosted sign-in with enterprise features, Amazon Cognito on AWS, Appwrite for an open-source backend, and Keycloak or Zitadel to self-host. To move, export users with their scrypt password hashes or let people link accounts, and keep each Firebase uid mapped to the new id, which loginos supports through external_id.

Firebase Authentication signs people in to web and mobile apps with very little code, and for many apps it stays a good choice. Teams start looking elsewhere when they need enterprise features, want to depend less on Google Cloud, run on many domains, or outgrow custom claims. Below: what Firebase Auth does well, why teams move, the main alternatives, and how to migrate without losing anyone.

## What is Firebase Auth good at?

Firebase Authentication is Google’s hosted sign-in service, and it is strongest when your app already lives in Firebase:

- **SDKs everywhere:** Apple platforms, Android, the web, Flutter, Unity and C++, plus FirebaseUI, an open-source drop-in sign-in UI.
- **Many sign-in methods:** email and password, email links, phone numbers with SMS codes, anonymous accounts, and providers such as Google, Apple, Facebook, GitHub, Microsoft, Twitter and Yahoo.
- **Guest accounts that convert:** an anonymous user can add an email or provider later with `linkWithCredential` and keep their data.
- **Security Rules:** Firestore, Realtime Database and Cloud Storage rules read the signed-in user as `request.auth`, so many apps need no server for access control.

If those features cover you, moving away buys little.

## Why do teams move away from Firebase Auth?

### Advanced features need Identity Platform

SAML and OIDC sign-in, multi-factor authentication, blocking functions and multi-tenancy aren’t part of standard Firebase Authentication. You get them by upgrading to Firebase Authentication with Identity Platform, a Google Cloud product. The upgrade needs no code migration, but it ties sign-in more closely to Google Cloud, and SAML there works on the web only.

### Password hashes use Firebase’s own scrypt

`firebase auth:export` writes every account to JSON or CSV, with password hashes and salts if your role is Editor or Owner. The hashes use what Firebase calls its internally modified version of scrypt, so a new provider must support that variant or check passwords against Firebase some other way. You also need the project’s hash parameters, `base64_signer_key`, `base64_salt_separator`, `rounds` and `mem_cost`, from the console.

### Every redirect domain must be authorized

Firebase only redirects to domains on the project’s **Authorized domains** list, added one full domain name at a time. Because browsers block third-party storage, redirect sign-in also needs `authDomain` on the same site as your app: a custom domain on Firebase Hosting, a reverse proxy for `/__/auth/`, or popups instead. Chrome 115 and later have required this since 24 June 2024. For a product on many domains, or on customers’ own domains, that is setup work for every new domain.

### Custom claims are capped at 1,000 bytes

Custom claims are extra fields in the ID token and the usual place for roles. The payload must not exceed 1,000 bytes, and new claims reach the client only with a new ID token: after the next sign-in, when the old token expires, or when the app calls `getIdToken(true)`. Apps with many roles or per-team permissions often keep them in Firestore instead.

## What are the alternatives?

| Alternative | Runs where | Keeping Firebase passwords |
| --- | --- | --- |
| Supabase Auth | Hosted, or self-hosted | Official import tools that ask for your hash parameters |
| Clerk | Hosted | Imports hashes with the `scrypt_firebase` hasher |
| Auth0 | Hosted | Automatic migration checks each password once, at sign-in |
| Amazon Cognito | Hosted on AWS | A migrate user Lambda trigger checks each password at sign-in |
| Appwrite | Hosted, or self-hosted | Creates users from “Scrypt modified” hashes |
| loginos | Hosted | Not needed: people link their accounts |

**Supabase Auth** keeps users in your project’s Postgres database and works with Row Level Security, which filters rows per user inside Postgres. It offers passwords, magic links, one-time codes, phone sign-in, social logins and, on Pro plans and above, SAML 2.0. Supabase is open source and can be self-hosted. Its `firestoreusers2json` and `import_users` tools move Firebase accounts into `auth.users`.

**Clerk** is hosted, with prebuilt components and organizations. Its Firebase guide imports users with `password_hasher: "scrypt_firebase"` and stores each Firebase uid as `external_id`, so passwords keep working and old data stays reachable.

**Auth0**, part of Okta, brings Organizations and enterprise connections. Its bulk import documents standard scrypt but not Firebase’s variant. Automatic migration avoids that: the first time someone signs in, your script checks the password against the old system and Auth0 creates the user.

**Amazon Cognito** suits apps already on AWS. CSV import can carry bcrypt, standard scrypt, Argon2id or PBKDF2 hashes on eligible user pools, but not Firebase’s modified scrypt. The migrate user Lambda trigger fills that gap by checking each password against Firebase at the first sign-in.

**Appwrite** is an open-source backend under the BSD-3-Clause license that you can self-host with Docker. Its migration wizard reads Firebase users, and its Users API creates accounts from Firebase’s “Scrypt modified” hashes. OAuth users aren’t migrated: they sign in again with their provider.

**Self-hosted identity providers** like Keycloak or Zitadel give you full control and SAML, at the cost of running a server. See [simpler alternatives to Keycloak](https://loginos.io/answers/keycloak-alternatives.md).

## How do you migrate users off Firebase Auth?

1. **Export the accounts.** Run `firebase auth:export users.json --format=json --project <id>` with an Editor or Owner role, and copy the password hash parameters from **Authentication > Users** in the console. Treat both as secrets. Accounts imported earlier with another algorithm export with empty hash fields until they sign in once.
2. **Keep the user id mapping.** The export calls the Firebase uid `localId`, and your Firestore documents, Storage paths and Security Rules probably use it. Keep it as the new user id if the provider allows that, or store it next to the new id. Never match old and new accounts by email unless the new provider verified the address.
3. **Choose a password strategy:** import the hashes, check each password at the first sign-in, ask everyone to reset, or link accounts so passwords never move.
4. **Plan for social accounts.** Accounts from Google, Apple or GitHub have no password. The export lists each provider’s user id as `rawId` under `providerUserInfo`, which helps you match them at the new provider.
5. **Keep Firebase services working during the move.** If Firestore or Storage stay, your server can turn a sign-in at the new provider into a Firebase session. It creates a custom token for the mapped uid with `getAuth().createCustomToken(uid)`, and the client signs in with `signInWithCustomToken()`. Security Rules keep seeing the same `request.auth.uid`.

## Where loginos fits

loginos is a hosted OpenID Connect provider for products on many domains. For a Firebase migration, the useful part is account linking: password hashes never leave Firebase.

Each person connects their Firebase-backed account to loginos once, from your settings page, while still signed in the old way. Your server sends their Firebase uid as `external_id` in a pushed authorization request (PAR, RFC 9126), so the browser never holds it. The link is made when your server redeems the code, and every loginos token after that carries the uid.

```js title="Express"
import crypto from "node:crypto";
import { getAuth } from "firebase-admin/auth";

const b64url = (b) => b.toString("base64url");
const basic = "Basic " + Buffer.from(`${process.env.LOGINOS_APP_KEY}:${process.env.LOGINOS_APP_SECRET}`).toString("base64");

// "Link loginos" on your settings page, for someone still signed in with Firebase.
app.get("/settings/loginos/link", async (req, res) => {
  const me = await getAuth().verifySessionCookie(req.cookies.session ?? "", true).catch(() => null);
  if (!me) return res.redirect("/login");

  const state = b64url(crypto.randomBytes(16));
  const verifier = b64url(crypto.randomBytes(32));
  const r = await fetch("https://loginos.io/oauth/par", {
    method: "POST",
    headers: { authorization: basic },
    body: new URLSearchParams({
      response_type: "code",
      redirect_uri: `https://${req.get("host")}/api/auth/login/single/callback`,
      scope: "openid profile email",
      state,
      code_challenge: b64url(crypto.createHash("sha256").update(verifier).digest()),
      code_challenge_method: "S256",
      external_id: me.uid, // the Firebase uid your data already uses
      external_label: me.email ?? me.uid,
    }),
  });
  if (!r.ok) return res.status(502).send("Couldn't start the link.");
  const { authorize_url } = await r.json();
  res.cookie("lo_link", `${state}.${verifier}`, { httpOnly: true, secure: true, sameSite: "lax", maxAge: 1_800_000 });
  res.redirect(authorize_url);
});
```

The callback is your normal sign-in callback: check `state`, redeem the code with the verifier, and confirm that `user.external_id` equals the uid of the person who started the link. From then on, “Sign in with loginos” returns that uid, and the custom-token step above keeps your Firestore rules working. People who join through loginos first get your id with `PUT /api/v1/users/{sub}/external-id`. The [linking guide](https://loginos.io/docs/link-accounts.md) has the whole flow, and [migrating without a new sign-up](https://loginos.io/answers/migrate-users-without-new-signup.md) compares the approaches.

**loginos fits** when you want to keep Firebase uids and skip the password migration, when your product runs on many domains that share one user pool, and when you want passkeys on every domain plus roles and permissions in every token. Domains can be added in the console or [from your server](https://loginos.io/docs/domains.md#from-your-server), and loginos has no paid plans today.

**loginos doesn’t fit** if you depend on sign-in methods it lacks: Google, Apple or other social logins, phone codes, anonymous accounts or email links. It also has no SAML, no multi-factor codes, no tenants inside a platform, no self-hosting and no login page on your own domain. loginos doesn’t verify email addresses yet, so never link accounts by matching email.

## Frequently asked questions

### Can I export password hashes from Firebase Auth?

Yes. `firebase auth:export` writes all accounts to JSON or CSV, with password hashes and salts when you have the Editor or Owner role. The hashes use Firebase’s modified scrypt, so you also need the project’s hash parameters from the console.

### Will users have to reset their passwords?

Not necessarily. Clerk and Appwrite can import Firebase’s scrypt hashes. Auth0 and Amazon Cognito can check each password against Firebase at the first sign-in, and account linking avoids moving passwords at all. A forced reset is the fallback, and it adds a step for every user.

### Can I keep Firebase Auth and move my data first?

Yes. Supabase, for example, accepts Firebase ID tokens as a third-party auth provider, so you can query Supabase with Row Level Security while people still sign in with Firebase. Each user needs a `role: 'authenticated'` claim, which you add with a blocking function or an `onCreate` function.

---

Updated 2026-10-14. Web page: https://loginos.io/answers/firebase-auth-alternatives
