SecureStartKit
SecurityFeaturesPricingDocsBlogChangelog
Sign inBuy Now
Jun 19, 2026·Security·SecureStartKit Team

Migrate Firebase to Supabase: 3 Security Traps [2026]

Most Firebase to Supabase guides port the data and stop. The 3 security-critical steps: Security Rules to RLS, the cutover window, the old project.

Summarize with AI

On this page

  • Table of Contents
  • Why is migrating from Firebase to Supabase a security event?
  • How do you translate Firebase Security Rules to Supabase RLS?
  • How do you move Firestore documents into Postgres without losing integrity?
  • How do you migrate Firebase Auth without breaking logins?
  • What leaks during the dual-write cutover window?
  • What do you lock down after cutover?
  • Landing the migration on a secure foundation

On this page

  • Table of Contents
  • Why is migrating from Firebase to Supabase a security event?
  • How do you translate Firebase Security Rules to Supabase RLS?
  • How do you move Firestore documents into Postgres without losing integrity?
  • How do you migrate Firebase Auth without breaking logins?
  • What leaks during the dual-write cutover window?
  • What do you lock down after cutover?
  • Landing the migration on a secure foundation

Migrating from Firebase to Supabase is a security event, not just a data export. The data tooling is mature: Supabase's importer copies a Firestore collection straight into a Postgres table for you [1]. The risk lives in three places those tools never touch. The Security Rules you have to rewrite as Row Level Security. The cutover window where two backends disagree about who is logged in. And the old Firebase project you forget to lock down.

A generic migration tutorial walks you through firestoreusers2json, an import script, and a find-and-replace of the client SDK, then declares victory. That gets your data into Postgres. It does nothing about the authorization model, which does not survive the move on its own, because Firestore Security Rules and Row Level Security (RLS) are different systems that fail in different ways.

This post is the security-critical path through the same migration. It assumes you have already decided to move; if you are still weighing the two backends, the Supabase vs Firebase comparison covers that decision. Here we focus on the parts where a careless migration leaks data.

TL;DR:

  • Trap 1: Security Rules do not map 1:1 to RLS. Firestore rules run in a rules layer keyed to Firebase Auth. RLS is enforced inside Postgres, like "adding a WHERE clause to every query" [4]. You rewrite every rule by hand, and the default posture flips.
  • Trap 2: the dual-write cutover window. While both backends run in parallel, a user can be authenticated in one and absent in the other, and a write can land in one store but not the other. That window is where auth-state and data-integrity bugs leak.
  • Trap 3: the old Firebase project left live. After cutover the source project still holds your data, its API keys, and possibly test-mode rules that allow "anyone to overwrite your entire database" [3]. Decommission it deliberately.
  • Firebase Auth uses scrypt; Supabase uses bcrypt. Password hashes do not transfer transparently. Plan for a forced reset or a first-login re-verification, and do not keep Firebase Auth running as a silent fallback.
  • Land on a deny-by-default destination. Enable RLS before you import a single row, so the window where data sits in Postgres with no policy never exists.

Table of Contents

  • Why is migrating from Firebase to Supabase a security event?
  • How do you translate Firebase Security Rules to Supabase RLS?
  • How do you move Firestore documents into Postgres without losing integrity?
  • How do you migrate Firebase Auth without breaking logins?
  • What leaks during the dual-write cutover window?
  • What do you lock down after cutover?
  • Landing the migration on a secure foundation

Why is migrating from Firebase to Supabase a security event?

Because the authorization model is the one thing that does not come across in the export. Your documents become rows and your users become auth records, but the rules that decided who could read what stay behind in Firebase. You rebuild that layer from scratch in a system with different semantics, and any gap between the old rules and the new policies is a hole an attacker can walk through.

The two systems guard data at different layers. Firestore Security Rules are evaluated by Firebase's backend before a request touches the data, and they only protect access through the Firebase SDKs. Supabase enforces RLS inside Postgres itself, so the same policy applies whether the query comes from the client library, a Server Action, or "third-party tooling" connecting straight to the database [4]. That is a stronger guarantee, but only if you write the policies. Until you do, a freshly imported table is governed by nothing.

So the migration has three security-critical stages that map to the three traps: rewriting the rules, surviving the cutover window, and decommissioning the source. The data copy is the easy part. The next sections take each stage in turn.

How do you translate Firebase Security Rules to Supabase RLS?

You rewrite every rule as an RLS policy by hand, because there is no automatic converter and the default posture is different. The single most important difference: in Firestore, the dangerous default is the "test mode" ruleset that grants open access. In Supabase, "once you have enabled RLS, no data will be accessible via the API when using a publishable key, until you create policies" [4]. One fails open, the other fails closed.

Start by finding out what your Firebase rules actually are. The Firestore quickstart ships a test-mode ruleset that looks like this, and the docs attach a blunt warning to it:

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;
    }
  }
}

Firebase's own wording is "NEVER use this rule set in production; it allows anyone to overwrite your entire database" [3]. If your project ever ran in test mode and you never tightened it, that is the security state you are migrating away from, and it is the reason a deny-by-default destination is an upgrade rather than a lateral move.

A real ownership rule translates cleanly once you see the mapping. A Firestore rule that restricts a document to its owner looks like this:

match /documents/{docId} {
  allow read, write: if request.auth.uid == resource.data.ownerId;
}

The Postgres equivalent enables RLS on the table, then adds a policy that compares the row's owner column to the authenticated user. request.auth.uid becomes auth.uid(), and resource.data.ownerId becomes a real column:

alter table documents enable row level security;

create policy "Owners can read their documents"
on documents for select
to authenticated
using ( (select auth.uid()) = owner_id );

create policy "Owners can write their documents"
on documents for insert
to authenticated
with check ( (select auth.uid()) = owner_id );

Two details matter. Wrapping the call as (select auth.uid()) lets Postgres evaluate it once per query instead of once per row, which is the difference between a fast policy and a slow one. And for select and for insert are separate policies, because a Firestore allow read, write collapses two operations that RLS makes you state explicitly. If you only write the select policy, inserts silently fail. The RLS policy generator scaffolds these from a table schema, and the RLS patterns guide covers the gotchas in depth. The work here is not hard, but it is manual, and every collection you skip is a table with no protection at all.

How do you move Firestore documents into Postgres without losing integrity?

You flatten each collection into a table and decide which embedded data needs to become its own relational structure. Supabase's tooling handles the mechanical part: "the process copies the entire contents of a single Firestore collection to a single Postgres table" [1], flattening it "to a table with basic columns of one of the following types: text, numeric, boolean, or jsonb" [1]. The integrity work is everything that flattening hides.

Firestore documents are denormalized by design. A user document might embed an array of orders, a nested settings object, and a stray isAdmin boolean. Flatten that and you get one wide row, with the nested pieces dropped into jsonb columns. For irregular metadata, jsonb is fine. For anything that another table should reference, or that a policy needs to read, it is a trap. As the Supabase guide notes, "if your structure is more complex, you can write a program to split the newly-created json file into multiple, related tables before you import" [1].

The security angle is the embedded permission field. In Firestore it was common to store something like isAdmin or role on the user document, then guard it with a rule. After the move, that value must become a column that RLS governs, not a field the client can still set. If a client could write its own role in Firebase and you carry the shape over verbatim, you have rebuilt a privilege-escalation bug in a new database. Decide during the migration which fields are authorization inputs, move them to columns the user cannot write, and enforce that with a policy. This is the same backend-only discipline covered in the backend-only data access guide: the browser never gets to assert its own permissions.

Building this from scratch on a new SaaS?

SecureStartKit ships every pattern in this post out of the box: backend-only data access, Zod on every Server Action, RLS deny-all, signed Stripe webhooks with idempotency dedup. One purchase, lifetime updates.

See what's included →Live demo

How do you migrate Firebase Auth without breaking logins?

You export users with firestoreusers2json and load them with import_users, but you plan for the password-hash mismatch up front. Supabase's tooling "exports users from an existing Firebase project to a .json file" and then "imports users from a saved .json file into your Supabase project, inserting those users into the auth.users table" [2]. The accounts move. The passwords are the sharp edge.

Firebase hashes passwords with scrypt; Supabase uses bcrypt. They are not interchangeable, so an imported user's hash cannot simply be verified by Supabase the way it was by Firebase. The migration guide has you copy your Firebase project's hash parameters (base64_signer_key, base64_salt_separator, rounds, and mem_cost) precisely so the migration tooling can re-verify a password against the original scrypt hash [2]. You have two honest options, and both have security consequences:

  • First-login re-verification. On a user's next sign-in, verify the supplied password against the carried-over scrypt hash, then write a fresh bcrypt hash. This keeps logins seamless, but it means your migration code temporarily handles raw passwords and the original Firebase hashes. Treat that code as security-critical and short-lived.
  • Forced password reset. Skip hash carryover and send every user through the reset flow. Cleaner, but a mass reset email is itself an attack surface. If you take this route, the Supabase authentication guide covers hardening the reset path against token leaks and enumeration.

Whichever you pick, the rule is the same: do not leave Firebase Auth running as a quiet fallback that you "might still need." A live auth system you are no longer watching is a live attack surface. Set a cutover date, migrate the sessions, and turn it off.

What leaks during the dual-write cutover window?

The window where both backends run in parallel is where auth state and data integrity drift apart. Migrating one feature at a time and keeping Firebase live during testing is the right strategy, but it creates a period where a user can be authenticated in Firebase and absent in Supabase, or a write can land in one store and never reach the other. Every one of those gaps is a place where a check passes that should have failed.

Two failure modes dominate this window. The first is split authorization state: a user whose role was updated in Postgres still carries the old claim from a Firebase session, so a stale token authorizes an action the new rules forbid. The second is the double-write race: an order written to Firestore during the sync, but read back from Postgres before the sync caught up, looks like it vanished, and a retry can create it twice. Make the sync idempotent, keyed on a stable document ID with a unique constraint, so a replayed write updates rather than duplicates.

Keep the window short and one-directional. Pick a moment when writes move to Supabase and Firebase becomes read-only, rather than letting both accept writes indefinitely. The longer two systems both believe they are the source of truth, the more reconciliation bugs you accumulate, and reconciliation bugs in an authorization context are security bugs. The vibe-coded migration playbook covers the same audit-then-cutover discipline for a different starting point.

What do you lock down after cutover?

Everything in the source Firebase project that still works. The migration feels finished when Supabase serves all your traffic, but the Firebase project is still sitting there with your data, its API keys, and whatever Security Rules it had on the day you stopped looking. If those rules were ever in test mode, the project is an open database that "allows anyone to overwrite your entire database" [3], and now nobody is watching it.

Run a deliberate decommission checklist:

  • Tighten or delete the Firestore rules. Replace any permissive ruleset with a deny-all, or delete the database. A migrated-away project should not accept reads or writes from anyone. Remember Firebase's own note that rule changes "can take up to 10 minutes to fully propagate" [3], so verify after, do not assume.
  • Revoke and rotate the old keys. Firebase config keys and any service-account credentials are still valid until you disable them. Pull them from your codebase and your deployment environment. The API key leak guide covers rotation order so you do not lock yourself out mid-switch.
  • Audit the new environment for stragglers. Make sure no NEXT_PUBLIC_FIREBASE_* variables survived the move into your Supabase deployment, where they leak the old project to the browser. The environment variable leak guide covers the scoping rules.
  • Delete the project when you are sure. Once data is verified in Postgres and the dual-write window is closed, the safest state for the old project is gone. An idle Firebase project is pure liability.

The service role key that now powers your Supabase admin access deserves the same care the Firebase keys did: server-only, never in the browser, rotated if it ever appears in a log.

Landing the migration on a secure foundation

A Firebase to Supabase migration is finished when three things are true: every collection has an RLS policy, no user can authenticate through a system you no longer monitor, and the source project is locked down or deleted. The data copy is a solved problem. The authorization model, the cutover window, and the decommission are the parts that decide whether the move made you safer or just moved your data to a new database with the old holes intact.

The cleanest way to avoid rebuilding Firebase's footguns is to land on an architecture that already fails closed. SecureStartKit ships the Supabase destination shape by default: RLS deny-all on every table, the service role key confined to the server, Zod on every input, and backend-only data access so the browser never holds a credential or asserts its own permissions. The createAdminClient() and RLS patterns in the template are the end state a migration should aim for, not a migration tool. Walk the SaaS security checklist against your new Supabase project before you delete the Firebase one, and if you would rather inherit the secure foundation than assemble it, that is what SecureStartKit is, and this site is the demo.

Built for developers who care about security

SecureStartKit ships with these patterns out of the box.

Backend-only data access, Zod validation on every input, RLS enabled, Stripe webhooks verified. One purchase, lifetime updates.

View PricingSee the template in action

References

  1. Migrate from Firebase Firestore to Supabase— supabase.com
  2. Migrate from Firebase Auth to Supabase— supabase.com
  3. Get started with Cloud Firestore Security Rules— firebase.google.com
  4. Row Level Security— supabase.com

Related Posts

Jun 1, 2026·Security

Supabase Storage Multi-Tenant RLS: 5 Leak Modes [2026]

Supabase Storage multi-tenant isolation: path-encoded RLS with tenant_id JWT claim, bucket-vs-path decision, and 5 cross-tenant leak modes.

May 17, 2026·Security

Supabase Multi-Tenancy + RBAC: The Secure Pattern [2026]

Multi-tenancy and RBAC in Supabase + Next.js. Tenant scoping via JWT claims + RLS, the composite index rule, and five cross-tenant leak modes.

May 16, 2026·Security

OWASP Top 10:2025 for Next.js + Supabase Apps

OWASP Top 10:2025 mapped to Next.js + Supabase failure modes plus the architectural defenses that prevent each category. With 2026 CVEs.