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 overview.
Prerequisites
- An Authdog tenant, project, and environment 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-jsAuth, and every policy or backend that callsauth.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
- Export — dump users, identities, and hash material from Supabase Auth.
- Map — translate Supabase fields into Authdog users, metadata, roles, and organizations.
- Import — load users into an isolated Authdog environment and validate.
- 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 |
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 |
| RLS / JWT claims | auth.uid(), auth.jwt() ->> 'role', custom claims |
Backend session validation + authorization |
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_passwordwhen 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 assignment |
| Tenant / org id in metadata | Organization 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:
- Enable password, magic link, social, MFA, and any enterprise SSO you need. See Sign up & sign in.
- Recreate Google, Apple, GitHub, and other providers with Authdog redirect URIs. Keep Supabase OAuth clients active until cutover.
- Decide how phone and anonymous users will authenticate after migration.
- Configure the hosted Account portal or components for the clients you move first.
5. Import users
Load users via console bulk import or the REST Directory API:
- Upsert each user with email, profile, and verification state.
- Set the Supabase Auth
idas the external identifier. - Link provider identities from
auth.identities. - Apply role / organization mappings derived from app metadata.
- Suspend banned users.
- 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:
- Replace
@supabase/supabase-jsAuth sign-in with Authdog (quickstarts) for the apps you cut over. You can keep Supabase as the database while Authdog becomes the identity provider. - Configure the Authdog environment public key (
pk_...). - Validate Authdog sessions on APIs instead of Supabase JWT secret / JWKS. See Backend requests.
- 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.
- 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
- Rehearse full export → import → sign-in in staging (including one RLS-critical flow).
- Move internal apps and a small production cohort.
- Shift web clients, then mobile, then long-lived jobs/functions.
- Monitor auth failure rates, RLS denials, and support volume.
- Disable new Supabase Auth sign-ups when Authdog is authoritative for a cohort.
- 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
idretained 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.