Move Supabase Auth users to Authdog while keeping `auth.uid()`-keyed data joins and Row Level Security working through external identifiers. This guide covers export from `auth.users`, password hashes, identity providers, JWT/RLS cutover, and retiring Supabase Auth. For product positioning, see the [Migrate from Supabase](/migrate-from-supabase) overview.

## Prerequisites

- An Authdog [tenant, project, and environment](/docs/concepts/multi-tenancy) for rehearsal (staging first).
- Supabase project access with permission to read Auth schema data (service role or SQL on `auth.users` / `auth.identities`) and list enabled providers.
- Inventory of every client that uses `@supabase/supabase-js` Auth, and every policy or backend that calls `auth.uid()`, `auth.jwt()`, or verifies Supabase JWTs.
- List of sign-in methods in use (email/password, magic link, phone, Google, Apple, GitHub, SAML, anonymous, and so on).

## Migration shape

1. **Export** — dump users, identities, and hash material from Supabase Auth.
2. **Map** — translate Supabase fields into Authdog users, metadata, roles, and organizations.
3. **Import** — load users into an isolated Authdog environment and validate.
4. **Cut over** — move clients in cohorts, update RLS/backends, then disable Supabase Auth writes.

Keep Supabase Auth serving production until Authdog acceptance checks pass.

## 1. Inventory Supabase Auth

Capture configuration your apps depend on:

| Supabase concept | What to capture | Authdog target |
| --- | --- | --- |
| `auth.users` | `id`, email, phone, `email_confirmed_at`, `banned_until`, metadata | [Users](/docs/users) |
| `auth.identities` | Provider, provider user id, identity data | Linked identities / connections |
| App / user metadata | `raw_app_meta_data`, `raw_user_meta_data` | Roles, org membership, or metadata |
| Password hashes | `encrypted_password` (bcrypt) when present | Password import or progressive migration |
| Providers | Google, Apple, GitHub, SAML, etc. | [Integrations](/docs/integrations) |
| RLS / JWT claims | `auth.uid()`, `auth.jwt() ->> 'role'`, custom claims | Backend session validation + [authorization](/docs/permissions) |

Anonymous users and phone-only users need an explicit product decision: import, convert on next sign-in, or drop.

## 2. Export users

Export with the Auth Admin API (`listUsers`) or SQL against the Auth schema. For each user retain:

- Supabase user `id` (UUID) — **required** external identifier for RLS and FK joins
- Email, email confirmation timestamp / verified state
- Phone and confirmation state when used
- `raw_user_meta_data` / `raw_app_meta_data`
- Banned / deleted signals
- Identities: provider, `provider_id`, identity data
- `encrypted_password` when the user has an email/password credential
- Created / last sign-in timestamps for cohort planning

Store the Supabase `id` as the Authdog **external identifier** so Postgres tables and RLS patterns that key on `auth.uid()` can keep resolving after you switch the session issuer (see cutover below).

### Password strategy

Supabase email/password users typically use bcrypt hashes in `encrypted_password`:

- **Hash import** — when Authdog accepts the exported bcrypt hashes, import them so passwords keep working.
- **Progressive migration** — verify the password against Supabase (or GoTrue) on next login, then set the credential in Authdog and stop calling Supabase Auth for that user.
- **Magic link / OAuth-only** — move cohorts off passwords when hashes cannot be exported or users never had one.

Never ship raw credentials through logs or client apps. Encrypt export artifacts and delete them after cutover.

## 3. Translate metadata and authorization

Supabase projects often encode authorization in JWT claims and RLS:

| Typical Supabase pattern | Suggested Authdog mapping |
| --- | --- |
| `raw_app_meta_data.role` / `claims.role` | RBAC [role](/docs/permissions) assignment |
| Tenant / org id in metadata | [Organization](/docs/concepts/organizations) membership |
| Feature flags in `user_meta` | User metadata or billing entitlements |
| Fine-grained RLS on resources | Keep RLS, but base it on a stable user id column you continue to populate from Authdog’s external id / mapped UUID |

If your database rows already use the Supabase Auth UUID as `user_id`, preserving that value as the Authdog external identifier (and/or importing so your app continues to send that UUID into RLS helpers) avoids a data rewrite. Plan this mapping **before** import.

## 4. Recreate providers in Authdog

In the target Authdog environment:

1. Enable password, magic link, social, MFA, and any enterprise SSO you need. See [Sign up & sign in](/docs/authentication).
2. Recreate Google, Apple, GitHub, and other providers with Authdog redirect URIs. Keep Supabase OAuth clients active until cutover.
3. Decide how phone and anonymous users will authenticate after migration.
4. Configure the hosted [Account portal](/docs/account-portal) or [components](/docs/components) for the clients you move first.

## 5. Import users

Load users via console bulk import or the REST [Directory API](/docs/api):

1. Upsert each user with email, profile, and verification state.
2. Set the Supabase Auth `id` as the external identifier.
3. Link provider identities from `auth.identities`.
4. Apply role / organization mappings derived from app metadata.
5. Suspend banned users.
6. Import bcrypt passwords when compatible; otherwise mark users for progressive migration.

Validate on staging:

- Email/password login for hash-imported users
- Social login for linked providers
- A sample RLS-protected query path that still authorizes using the preserved user id

## 6. Update clients, RLS, and backends

Supabase JWTs and Authdog sessions are not interchangeable. For each surface:

1. Replace `@supabase/supabase-js` Auth sign-in with Authdog ([quickstarts](/docs/quickstarts)) for the apps you cut over. You can keep Supabase **as the database** while Authdog becomes the identity provider.
2. Configure the Authdog environment **public key** (`pk_...`).
3. Validate Authdog sessions on APIs instead of Supabase JWT secret / JWKS. See [Backend requests](/docs/backend).
4. Update RLS: policies that call `auth.uid()` only work for Supabase-issued JWTs. Options:
   - Pass the preserved Supabase UUID into Postgres via a secure RPC/session variable your backend sets after Authdog validation, or
   - Introduce an `authdog_user_id` / mapping table and revise policies in a controlled migration.
5. Rewrite Edge Functions that trusted `Authorization: Bearer <supabase-jwt>` to accept Authdog sessions.

During transition, a temporary backend adapter may accept Supabase JWTs **or** Authdog sessions. Instrument both paths and remove the Supabase Auth path per cohort.

## 7. Cut over in stages

1. Rehearse full export → import → sign-in in staging (including one RLS-critical flow).
2. Move internal apps and a small production cohort.
3. Shift web clients, then mobile, then long-lived jobs/functions.
4. Monitor auth failure rates, RLS denials, and support volume.
5. Disable new Supabase Auth sign-ups when Authdog is authoritative for a cohort.
6. After soak, disable Supabase Auth providers, rotate the JWT secret only when nothing still depends on it, and remove Auth usage from the Supabase project when safe.

## Acceptance checklist

- [ ] Every production Supabase Auth `id` retained as an Authdog external identifier.
- [ ] Password import or progressive migration verified for email users.
- [ ] Provider identities linked; Google / Apple / GitHub (as used) succeed on Authdog redirects.
- [ ] Metadata roles/tenancy mapped to Authdog roles or organizations.
- [ ] Clients no longer require Supabase Auth JWTs for migrated cohorts.
- [ ] RLS or equivalent checks updated for the new session model (no silent `auth.uid()` breakage).
- [ ] Banned users remain suspended in Authdog.
- [ ] Supabase Auth credentials / provider secrets revoked after the soak period.

## Related

- [Migrate from Auth0](/docs/guides/migrate-from-auth0)
- [Migrate from Firebase](/docs/guides/migrate-from-firebase)
- [Migrate from WorkOS](/docs/guides/migrate-from-workos)
- [Users](/docs/users)
- [Permissions](/docs/permissions)
- [Migrate from Supabase (overview)](/migrate-from-supabase)
