# 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.

Source: https://supahost.app/kb/migrate-from-auth0 · Updated 2026-11-11

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](/docs/auth/custom-claims-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):

```bash
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:

```ts
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:

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

Auth emails are sent by SupaHost from `noreply@supahost.app` 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:

```sql
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.

## Related

- [Managing users (admin API)](/docs/auth/user-management)
- [Every OAuth provider: setup reference](/docs/auth/oauth-providers)
- [Can I use Clerk, Auth0 or Firebase Auth instead of Supabase Auth?](/kb/third-party-auth)
- [Linking identities](/docs/auth/identity-linking)
