What is a good Firebase Auth alternative?
Short answer
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
linkWithCredentialand 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.
How do you migrate users off Firebase Auth?
- 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. - 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. - 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.
- Plan for social accounts. Accounts from Google, Apple or GitHub have no password. The export lists each provider’s user id as
rawIdunderproviderUserInfo, which helps you match them at the new provider. - 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 withsignInWithCustomToken(). Security Rules keep seeing the samerequest.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.
your-app.example/settings
Jane Doe
id u_1842
loginos.io
Your app account
jane@example.com
Your loginos account
Jane Doe @jane
What your server reads from /oauth/token
{ "sub": "9b1f6c2e-…", "preferred_username": "jane", "roles": [ "member" ]}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 has the whole flow, and migrating without a new sign-up 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, 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.
One account for every domain you run
loginos is an OpenID Connect provider with passkeys, a user pool shared by all of your domains, roles in every token, and account linking for the users you already have. Add your platform and its domains in a few minutes.
Keep reading
- Existing usersHow do I migrate existing users to a new login system without making them sign up again?Move the accounts, not the people. Bulk-import users with their password hashes if the new system can verify your hash format, or migrate each person at their next login by checking the old password once. You can also skip the migration: keep your accounts and let signed-in users link the new login. loginos takes that last path, so people keep their account and connect loginos once.6 min read
- Existing usersHow do I link existing user accounts to an OAuth login?Keep a table that maps each provider’s user id, the sub claim, to your own user id. Start the link from a page where the person is already signed in, send state and PKCE, and save the link on your server only after you exchange the code. Refuse a provider account that is already linked to someone else, and never link accounts by email.7 min read
- Customer domainsHow do I make Supabase auth work across multiple domains?You can’t share one Supabase session between different domains, because supabase-js keeps it in each site’s own local storage or cookies. Give every domain its own session instead, from a sign-in they all trust: an OpenID Connect provider that remembers the person, added to Supabase Auth as a custom provider. With loginos in front, the second domain signs in without a form.6 min read
- Choosing a providerWhat is a good alternative to Auth0?Good Auth0 alternatives include hosted services like Clerk, WorkOS AuthKit, Stytch, Descope and Kinde, and open-source servers you run yourself, like Keycloak, Zitadel, authentik, Ory and Logto. Firebase, Supabase and Amazon Cognito bundle sign-in with their platforms, and Auth.js or Better Auth keep it in your code. Choose by who runs it and how you would leave. loginos fits products on many domains.7 min read