Migrate from Auth0

Move Auth0 users into Supabase Auth on SupaHost: export users, bring over bcrypt password hashes, recreate social connections and roles, and switch your app's sign-in calls.

You move Auth0 users to SupaHost by exporting them from Auth0 and creating each one in your project with the Supabase Auth admin API. Auth0 stores passwords as bcrypt hashes, which Supabase Auth accepts, so password users can keep their passwords if you can get the hashes. Social users sign in again with the same provider.

Plan

  • Password hashes. Auth0's normal user export doesn't include them. You request a password hash export from Auth0 support, and whether it's offered depends on your Auth0 plan. If you can't get one, users reset their password after the move (see below).
  • IDs. Auth0 IDs look like auth0|65f1c2... or google-oauth2|1098.... Supabase Auth IDs are UUIDs, so keep the Auth0 ID in metadata and update foreign keys in your own tables.
  • Providers. Note each social connection you use. On SupaHost, set them up in Dashboard → your project → Auth tab → OAuth providers, with the callback https://app-one.acme.supahost.space/auth/v1/callback.
  • Roles. Auth0 roles and permissions have no direct equivalent; plan to keep them in app_metadata or a roles table (see Roles, custom claims and RBAC).
  • MFA. TOTP enrolments can't be moved; users enrol again.

Export from Auth0

Create an export job with the Management API (or use Auth0's User Import / Export extension):

curl -X POST "https://bookings.eu.auth0.com/api/v2/jobs/users-exports" \
  -H "Authorization: Bearer $AUTH0_MGMT_TOKEN" -H "Content-Type: application/json" \
  -d '{"format":"json","fields":[{"name":"user_id"},{"name":"email"},{"name":"email_verified"},{"name":"name"},{"name":"app_metadata"}]}'

Poll the job, download the gzipped NDJSON file, and merge in the password hashes from Auth0 support by user_id or email.

Load the users

Run this on your machine or a server, never in a browser:

import { createClient } from '@supabase/supabase-js'
import { readFileSync } from 'node:fs'

const admin = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE_KEY!, {
  auth: { persistSession: false, autoRefreshToken: false },
})
const lines = readFileSync('auth0-users.ndjson', 'utf8').trim().split('\n')

for (const line of lines) {
  const u = JSON.parse(line) // { user_id, email, email_verified, name, app_metadata, passwordHash? }
  if (!u.email) continue
  const { error } = await admin.auth.admin.createUser({
    email: u.email,
    email_confirm: !!u.email_verified,
    ...(u.passwordHash ? { password_hash: u.passwordHash } : {}), // "$2b$10$..."
    user_metadata: { auth0_id: u.user_id, name: u.name },
    app_metadata: { roles: u.app_metadata?.roles ?? [] },
  })
  if (error) console.error(u.email, error.message)
}

Users created without a hash get a random password. Social users who sign in with the same provider and a matching verified email are linked to their migrated account.

Try three or four of your own accounts first. If you have no hashes, send a reset email after the move:

await supabase.auth.resetPasswordForEmail(email, {
  redirectTo: 'https://bookings.example.com/account/new-password',
})

Auth emails are sent by SupaHost from [email protected] with your App name as the sender name. Auth email sending is rate limited, so for thousands of users a reset campaign is better sent from your own email provider with a link to your "forgot password" page.

Switch the app

Auth0 SDK supabase-js
loginWithRedirect() supabase.auth.signInWithOAuth({ provider: 'google' }) or signInWithPassword
getAccessTokenSilently() supabase.auth.getSession()
logout() supabase.auth.signOut()
Rules / Actions adding claims app_metadata (included in the user's JWT) or a roles table

Then update ownership columns from Auth0 IDs to the new user IDs:

update public.bookings b
set user_id = u.id
from auth.users u
where u.raw_user_meta_data->>'auth0_id' = b.auth0_user_id;

Verify

Count users with select count(*) from auth.users, sign in as a password user and a social user, and check that your RLS policies return their bookings.

Updated 11 November 2026 · View as Markdown · Something wrong or missing? Tell us