← All playbooks
Authentication and identity migration

Auth0 → Supabase

Export the Auth0 directory, ask support for the password hashes, and create those people in Supabase Auth. Sessions, organizations, and Actions stay behind.

Typical timelineAbout 2 to 6 weeks. The hash export has no promised date.A few days of mapping once both files are in hand active work
Statusneeds review
Source profileAuth0 bulk export, hash export, and export-data docs reviewed 2026-11-07
Destination profileSupabase Auth0 migration guide and identity linking docs reviewed 2026-11-07
Documentation date
Review window
Evidence7 sources
This route needs review.Use it as a planning baseline, then verify the dated sources and test with representative data before production.
Before you migrate

Should you make this move?

Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.

Current platform

Auth0

Reasons to stay
  • Mature federation, extensibility, enterprise connections, and migration tooling cover complex identity programs
  • Centralized sign-in, federation, MFA, and user lifecycle controls reduce application security work
Reasons to leave
  • Tenant configuration, actions, pricing, and Auth0-specific claims can create substantial lock-in
  • Passwords, sessions, factors, identities, and authorization policy are unusually sensitive to migration errors
New platform

Supabase

What gets better
  • PostgreSQL, authentication, storage, realtime, and edge functions offer an open backend stack
  • Managed infrastructure removes a large amount of operational work
What gets worse
  • Production scaling and service integration still demand database and infrastructure judgment
  • Platform-specific services can create architectural lock-in
Best of the move

Supabase: PostgreSQL, authentication, storage, realtime, and edge functions offer an open backend stack. This removes a major source-side concern: Tenant configuration, actions, pricing, and Auth0-specific claims can create substantial lock-in.

Worst of the move

What you lose: Mature federation, extensibility, enterprise connections, and migration tooling cover complex identity programs. What you inherit: Production scaling and service integration still demand database and infrastructure judgment.

Jump to a section
01At a glance

Know the shape of the move.

Transfer outcome12 features audited
Transfer outcome distributionClean transfer: 0, Partial transfer: 5, Manual rebuild: 5, Not transferred: 2.
Clean0
Partial5
Manual5
Lost2
Mapping route7 of 10 fields have a destination path

This timeline assumes

  • The Auth0 tenant is on a paid plan. Bulk user export and the password-hash export are both unavailable on the free plan.
  • You can open a support case. If Auth0 approves the hash export, a second tenant admin confirms it unless you are the only admin, and a CISO, CSO, or VP signs the form.
  • Auth0 stays up through the rollback window. You can still turn its connections back on.
  • The app can accept an Auth0 JWT and a Supabase JWT during the overlap. The site already documents Auth0 → Clerk, Auth0 → Keycloak, and Auth0 → Firebase Authentication.
  • Firestore, storage, and the rest of a Firebase backend are a different move. That one is Firebase → Supabase.
02What transfers

What survives the move.

“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.

ItemOutcomeImpactWhat happensMitigation
Database passwordspartialcriticalThe bulk export and the Management API do not include password hashes. Auth0 sends them only through a support case, PGP-encrypted, and not on the free plan. Supabase can take a bcrypt or argon2 hash on createUser.Open the case before you write the import. Sign in as a test user whose password you know after the row lands.
Custom database connectionsmanualcriticalIf the password lives in your database, Auth0 never stored the hash, unless you already ran a progressive migration into Auth0.List every connection. Users with no hash need a reset, or the old login check stays up until they have signed in once.
Passwordless email and phonepartialhighSupabase's guide says to create a confirmed user, then send the code with signInWithOtp. There is no password to copy.Mark email_confirm or phone_confirm from Auth0's verified flag. Send one test code before you tell the list.
Social loginspartialcriticalsignInWithOAuth does not need a pre-created row. The person signs in at Google or GitHub again. Auth0 stores that provider id as provider|id, for example github|123456.Reuse the same OAuth client id and secret if you want to skip a fresh consent screen. Match the provider id to your existing app user.
Sessions and refresh tokenslosthighSupabase's rolling and one-off plans both say people may have to sign in again. An Auth0 refresh token is not a Supabase session.Ship the new client first. Keep Auth0 able to sign people in until the fallback path is quiet.
MFA factorsmanualhighMFA secrets are a separate support export, same PGP path as the hashes. Supabase Auth only supports TOTP, and the migration guide says those people may need to enroll again.Do not promise a silent MFA move. Have one TOTP user enroll on Supabase before cutover.
OrganizationslostcriticalSupabase's Auth0 guide says migrating Auth0 organizations is not supported.Rebuild membership in your own tables, or pick a destination that already has orgs. Auth0 → Clerk is the other route on this site.
SAML connectionsmanualcriticalThe customer has to attach the identity provider on the Supabase side. The guide tells you to contact Supabase support if you want the existing Auth0 SAML connections moved. SAML users are left out of automatic and manual identity linking.Pilot one company before you tell the rest. Do not expect a shared email to merge a SAML user into a password user.
Actions, Rules, and hooksmanualhighThe user export is profiles. Actions, Rules, and custom database scripts do not come with it. Auth0 notes that the same libraries are on npm, which means you rewrite the code.List every hook that still changes a token or a profile. Test it against one copied user.
User idspartialcriticalSupabase user ids are UUID v4. You can pass an id only when you already have one in that form. An Auth0 id such as auth0|abc is not a UUID.Let Supabase assign the id. Store the Auth0 user id on app_metadata or in your own user table.
Duplicate emailsmanualhighAutomatic linking attaches identities that share a verified email to one Supabase user. Auth0 can hold separate accounts on that same email. Parqet had a few thousand of those, and about 100 they could not merge with a rule.Decide the surviving account before createUser. Write down the ones you skip.
Metadata and rolespartialhighuser_metadata is for fields the person can change. app_metadata is for fields they should not, such as a role. Both are copied into the access token. The hash export does not include this metadata. You merge it from the profile export.Keep the token small. Put a long profile in your own table keyed by the Supabase user id.
03Field and feature mapping

Where each thing goes.

SourceDestinationMethodNotes
Database user (identities provider auth0)auth.users via createUsertransformPass password_hash. Set email_confirm when Auth0 already verified the address.
Password hash from the support exportauth.users.encrypted_passwordtransformSupabase accepts bcrypt and argon2. New password users it creates itself use bcrypt.
Auth0 user_idapp_metadata or your user tablemanualJoin it to oid on the hash file, per the Supabase guide. Do not pass it as the Supabase id unless it is already a UUID.
user_metadatauser_metadatatransformThe person can update this. It also lands in the JWT.
app_metadata and rolesapp_metadatamanualRoles the user must not edit go here, still inside the JWT.
Social identityauth.identities after signInWithOAuthautomaticMatch provider|id to the old account. Same OAuth client id avoids a new consent screen.
Verified phone or email, no passwordConfirmed user, then signInWithOtptransformCreate the user with email_confirm or phone_confirm set.
TOTP factorNo destinationunsupportedYou can request the secret. The guide still says the person may enroll again.
OrganizationsNo destinationunsupportedSupabase's guide does not move Auth0 organizations, so rebuild membership yourself.
Sessions and refresh tokensNo destinationunsupportedPeople sign in again.
04Before you begin

Make the move recoverable.

Backup procedure

Create the source-of-truth backup

Keep a profile export you can re-read, and keep the decrypted hashes off the app repo.

  1. Count users per connection in the Auth0 dashboard.
  2. Run one sample export of 10 users and check the fields.
  3. Export each connection you are keeping. The dashboard job is per connection. The Management API exports every connection if you omit connection_id.
  4. Download the file. The location URL lives for 60 seconds. You can request that link again during the 24 hours before the job is deleted.
  5. Store the JSON outside the repo, with the counts and the job id.

Proof to capture: Per-connection counts, the export file location you archived, and the name of the admin who can rerun the job.

Transformation · Auth0 support case and gpg

Request the hash export before you need it

Passwords do not move until Auth0 support sends the PGP file.

  1. Generate an RSA 4096 key with gpg --full-generate-key. Pick RSA and RSA. An ECC key is rejected. The uid email has to match the tenant admin who opens the case, including capitalization.
  2. Export the public key with gpg --armor --export and the key id from gpg --list-keys. Stay under 35,000 characters. Set the key to expire no sooner than 7 days out.
  3. Open the case with the tenant name and the public key. Auth0 reviews eligibility and does not give an ETA.
  4. If they approve, confirm the export in writing. A second tenant admin confirms it too, unless you are the only admin. A CISO, CSO, or VP signs the acknowledgment. A typed name is not enough.
  5. Download within 3 days. Only the person who opened the case can download, and they still need the tenant admin role. Decrypt with gpg --output hashes.json --decrypt export.pgp. Never send Auth0 the private key.

Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.

Transformation · auth.admin.createUser

Create the Supabase users from the two files

The profile export and the hash export are different files. The import joins them.

  1. Filter database users whose identities include the provider auth0. Supabase's guide says to match that user_id to oid on the hash row. Read the file you received before you trust the column name.
  2. Call createUser with the email, password_hash, and email_confirm true when Auth0 already verified the mailbox. Use the service role on a machine that is not the browser.
  3. Put the Auth0 user id in app_metadata or in your own table. Pass id only when you already have a UUID.
  4. For a miss, try Supabase sign-in, then Auth0, and create the user only after Auth0 accepts the password. That is the fallback in the Supabase guide.
  5. Social users can wait until signInWithOAuth. Passwordless users are created confirmed, then they get a code.

Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.

05Handle with care

The things most likely to hurt.

These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.

Export

The hash file is late or short

criticallikely likelihood

Auth0 does not promise a delivery time. Parqet waited about a week for a test file and about a week and a half for the final one, which arrived at 2am on a Saturday.

Consequence
You import on their clock. Parqet found 1,600 people missing after more than 72,000 rows were already in, and those people needed a reset email.
Mitigation
Open the case first. Diff the hash file against the profile export before you disable Auth0.

Stop if: The hash file is missing database users and you have no reset path for them.

Import

Two Auth0 accounts share an email

highpossible likelihood

Supabase links a verified email to one user. Auth0 does not.

Consequence
The second account is dropped, or the wrong history gets attached. Parqet still had about 100 cases to do by hand.
Mitigation
Pick the surviving account from your own activity data before createUser.

Stop if: A sample login opens a different app profile than the one that owns the data.

Cutover

The new client is not in people's hands

highpossible likelihood

A web deploy is fast. A store review is not. Parqet's iOS release sat in App Review and held the toggles.

Consequence
You turn off Auth0 while the installed app can only talk to Auth0.
Mitigation
Ship the Supabase client, and keep the Auth0 path, before you disable a connection.

Stop if: The build people are running cannot verify a Supabase session.

Sessions

A mobile session dies after an hour

mediumpossible likelihood

Loper set autoRefreshToken and also refreshed the token themselves. Supabase treated that as spoofing and revoked it.

Consequence
People are logged out when they reopen the app, which looks like a failed migration.
Mitigation
Leave one device backgrounded past the access-token lifetime and sign a request after that.

Stop if: A test device is signed out with no password error and no expired-code error to explain it.

06Precise timeline

Do the work in this order.

Estimate forOne paid Auth0 tenant, database passwords plus at least one social provider, Supabase Auth already created
Total elapsedAbout 2 to 6 weeks. The hash export has no promised date.
Active workA few days of mapping once both files are in hand
BufferParqet's final hash file took about a week and a half and showed up at 2am. Loper, with codes and no passwords, finished in under a month.
01
Day 1Open the hash case and count the tenant1 day
02
Days 1 to 3Export profiles and find duplicate emails1 to 3 days
03
When the test hash file arrivesImport a handful of usersHowever long Auth0 takes, often more than a week
04
After the pilotShip the client that can talk to bothA few days, longer if a store has to review the build
05
When the final hash file arrivesBackfill passwords and turn providers over1 day plus the export wait
  1. Day 1 · case

    Open the hash case and count the tenant

    2 to 4 hours active1 day elapsedNone yet. The wait starts when the case is open. waiting
    • Count users per connection.
    • Note which connections are database, social, passwordless, or SAML.
    • Generate the PGP key and open the support case.
    • List organizations, Actions, and Rules you will rebuild.
    Stop / go checkpoint

    Can this tenant export hashes?

    Go when: The plan is paid, the case is open, and every connection has an owner.

    Stop when: The tenant is on the free plan, or a custom database holds the passwords and nobody has said so.

  2. Days 1 to 3 · profiles

    Export profiles and find duplicate emails

    Half a day active1 to 3 days elapsedThe export job waiting
    • Download a sample of 10, then the full JSON.
    • Group database users, social users, and duplicate emails.
    • Store the Auth0 id next to your app user id.

    Depends on: case

    Stop / go checkpoint

    Is the profile snapshot complete?

    Go when: Counts match the dashboard, and duplicate emails have a surviving account.

    Stop when: A connection is missing or the job expired before you downloaded it.

  3. When the test hash file arrives · pilot

    Import a handful of users

    Half a day activeHowever long Auth0 takes, often more than a week elapsedThe support export waiting
    • Decrypt the test file.
    • createUser for one password user, one social user, and one duplicate-email pair.
    • Sign in with the known password. Send one passwordless code if you use them.

    Depends on: profiles

    Stop / go checkpoint

    Does the join work?

    Go when: The known password works and the social user maps to the old app profile.

    Stop when: oid does not match user_id, or the test user is asked to verify an address Auth0 already verified.

  4. After the pilot · clients

    Ship the client that can talk to both

    A few days activeA few days, longer if a store has to review the build elapsedApp Review, if you have a native app waiting
    • Verify both JWTs in the API.
    • Add a toggle per login method.
    • Release the web app and the store build before you flip anything.

    Depends on: pilot

    Stop / go checkpoint

    Can a production build sign in with Supabase?

    Go when: The build people already have accepts a Supabase session and still accepts Auth0.

    Stop when: Only a local build can reach Supabase.

  5. When the final hash file arrives · backfill

    Backfill passwords and turn providers over

    A day active1 day plus the export wait elapsedThe final hash file waiting
    • Diff the hash file against the profile export.
    • Import the remaining database users.
    • Turn social sign-in to Supabase. Leave Auth0 up for the fallback.

    Depends on: clients

    Stop / go checkpoint

    Are the missing hashes explained?

    Go when: Every database user is in Supabase or on the reset list.

    Stop when: The file is short and the reset mail is not ready.

07The point of change

Cut over with a way back.

Go live

Cutover

New sign-ins go to Supabase, and Auth0 is still there if a password misses.

Recommended window: After the final hash file is imported and the client that understands Supabase is already what people are running. A file that lands at 2am can wait until the next day.

  1. Stop new signups on Auth0. Write down the time.
  2. Export profiles once more and import anyone created after the last file.
  3. Point each login method at Supabase. Keep the Auth0 password check as the fallback from the Supabase guide: Supabase first, Auth0 if that fails, createUser only after Auth0 accepts the password.
  4. When refreshes should die, disable that Auth0 social connection. Parqet did this so the old token failed and the next open went through Supabase. Do it only after the new client is out.
  5. Leave the Auth0 tenant billed. Do not delete it this week.

Proof to capture: The signup freeze time, the diff of the last profile export, and a password login plus one social login on production.

Return to safety

Rollback

Send sign-in back to Auth0 without throwing away the Supabase rows you may need to diff.

Deadline: While the Auth0 connections still exist and the client can still verify an Auth0 JWT.

  1. Point the login toggles back at Auth0.
  2. Re-enable any social connection you turned off.
  3. Leave the Supabase users in place.
  4. If someone signed up only on Supabase during the overlap, write that id down before you turn the new signup off.
  5. Read one Auth0 login and one token refresh before you call the rollback done.

Proof to capture: A production login through Auth0, and a list of accounts that exist only in Supabase.

Rollback immediately when
  • The known-password test user cannot sign in on Supabase.
  • A social login creates a second app profile.
  • The hash file is missing people and the reset mail has not gone out.
  • The installed client cannot verify a Supabase session.
08Verification report

Prove the migration worked.

Every blocking check must pass. Capture the evidence before cleanup begins.

0%
Interactive report preview0 / 9 checks passed
PassIDCheckMethodExpected resultEvidence
USR-01BlockingCounts per connectionCompare the Auth0 export counts with auth.users, split by database, social, and passwordless.Counts match, or the exception list names every skip.The count table.
PWD-01BlockingA known password still worksSign in on Supabase as the test user whose password you set in Auth0.The session is issued and the app loads the old profile.The user id and the time of the login.
HSH-01BlockingEvery database user is joined or listedDiff Auth0 user_id values against the hash file.No silent gaps. Missing rows are on the reset list.The diff and the reset list.
SOC-01BlockingProvider id maps to the old profileSign in with each social provider you still offer.The app user id is the one from before the move.The provider id and the app user id.
DUP-01BlockingShared emails have a decisionOpen the duplicate-email list and the Supabase user each one became.Each pair has a surviving account, and the other is not a second profile.The decision list.
MFA-01BlockingA TOTP user can enrollWalk one factor through enroll and a second factor prompt on Supabase.The test user enrolls on Supabase and passes a second prompt.The test user. Skip only if the tenant has no MFA, and write that down.
EML-01BlockingAlready-verified mailboxes stay verifiedSign in as a user Auth0 marked verified.Supabase does not send a fresh verification before the session.The user id and the confirm flag.
ORG-01BlockingOrg membership has a homeList Auth0 organizations and where each membership went.Each org is rebuilt in your tables or named as dropped.The org list. If you have none, the note says so.
APP-01BlockingThe API accepts the new tokenCall a signed-in route with a Supabase access token for a migrated user.The route returns the same app profile as an Auth0 token did.The response and the user id.
09Post-migration cleanup

Remove the scaffolding safely.

Safe after: A week of Supabase sign-ins with no Auth0 fallback hits, and the reset list is either mailed or empty.

  1. Delete the decrypted hash file from laptops and CI. Keep the PGP private key only while you still need to decrypt that download. The link itself dies after 3 days.
  2. Revoke the Management API token you used for the export.
  3. Remove the Auth0 JWT check only after the rollback window.
  4. Keep the tenant until finance has the last invoice and you are sure you will not need another hash export.
  5. Set the next review date.
10Experiences from the field

When the plan met reality.

First-hand accounts are preferred. Vendor case studies are labeled, and every note below is an editorial paraphrase—follow the link for the full context.

First-person accountKevin Grüneberg · Medium

Migrating 125,000 users from Auth0 to Supabase2022

Kevin Grüneberg describes Parqet moving 125,000 registered users from Auth0 to Supabase over a few weeks. The product had Google, Apple, Facebook, and email-password logins, a Nuxt web app, a native iOS app, and Circle single sign-on. They toggled each social provider first, then imported bcrypt hashes that Auth0 sent only after a support request. The final file arrived at 2am on Easter Saturday and was missing 1,600 people after more than 72,000 had already been imported, so those accounts got a generated password and a reset mail. A few thousand duplicate emails had to be merged, about 100 by hand, and an App Review hold delayed the iOS release.

What they recommend
  • Open the password-hash support case before the import, and ask for a test file. Parqet waited about a week, then another week and a half, and could not choose the delivery time.
  • Match linked accounts and duplicate emails before createUser. They noticed Auth0's link extension late and had to reconnect a slice of people afterward.
Worth noticing
  • The bcrypt hashes worked in Supabase without a rewrite, but they arrived as a support export, not in the normal user download.
  • Disabling an Auth0 social connection made old refreshes fail, so the next open went through Supabase. He still spent the following days on token refresh bugs and duplicate emails.
First-person accountSam Eigen · Sam's Blog

Loper authentication migration2024

Sam Eigen writes up Loper's rolling move from Auth0 to Supabase. Loper is a React Native and Expo app with more than 100,000 users, and sign-in was a code by email or phone. The post-startup Auth0 renewal was not a price they could pay, and they had less than a month, over the holidays. With no passwords stored, the first successful sign-in created the Supabase user and updated their own user row. About a month later the first Supabase bill was $50. The rough edge was sessions: autoRefreshToken plus their own refresh looked like spoofing, Supabase revoked the token, and people were logged out until that flag was turned off.

What they recommend
  • If the product is passwordless, create the user on the next login and keep Auth0 able to send codes until the people you still care about have signed in once.
  • On React Native, leave a session past the access-token lifetime before you call it done. Loper's logout bug showed up when people reopened the app.
Worth noticing
  • He had less than a month because the Auth0 renewal was the deadline, and the first Supabase bill a month later was $50.
  • They kept Twilio and Mandrill, configured inside Supabase. The logout bug was the autoRefreshToken flag on the React Native client.
Sources and evidence

Verify against the primary material.

Platform behavior changes. Check these sources and the review dates above before executing a production migration.

  1. Supabase: migrate from Auth0Accessed 2026-11-07
  2. Supabase: identity linkingAccessed 2026-11-07
  3. Auth0: bulk export user dataAccessed 2026-11-07
  4. Auth0: export password hashes and MFA secretsAccessed 2026-11-07
  5. Auth0: export dataAccessed 2026-11-07
  6. Kevin Grüneberg: migrating 125,000 Parqet users from Auth0 to SupabaseAccessed 2026-11-07
  7. Sam Eigen: Loper's Auth0 to Supabase migrationAccessed 2026-11-07