Auth Migration
Move users to Better Auth — without making anyone reset their password.
This is the hard part of leaving Supabase, and Unbase's headline feature.
The problem
Supabase Auth (GoTrue) stores credential passwords as bcrypt hashes. Better Auth verifies with scrypt by default. Naively migrating users would leave everyone unable to log in — forcing a password reset for your entire user base.
How Unbase solves it
extract pulls auth.users and auth.identities into a normalized auth model,
keeping each user's bcrypt hash verbatim. auth then writes Better Auth rows:
user— id, email,emailVerified(fromemail_confirmed_at), name and image from user metadata.account(credential) —providerId: "credential"carrying the original bcryptpassword.account(OAuth) — one per linked identity (google,github, ...), keyed by provider + provider account id.
Account ids are deterministic, so re-running auth is idempotent.
The one config you must add
Tell Better Auth to verify bcrypt hashes:
import bcrypt from "bcryptjs";
export const auth = betterAuth({
// ...
emailAndPassword: {
enabled: true,
password: {
hash: (password) => bcrypt.hash(password, 10),
verify: ({ hash, password }) =>
/^\$2[aby]\$/.test(hash)
? bcrypt.compare(password, hash)
: Promise.resolve(false),
},
},
});With this, migrated users log in with their existing passwords, and new sign-ups are hashed with bcrypt too — one uniform scheme.
Users with no email are skipped (Better Auth requires one) and reported in the
auth output. Sessions are not migrated — users simply log in again.
Other targets
npx unbase auth --to authjs # Auth.js (NextAuth) instead of Better AuthThe auth mapping is pluggable. Auth.js is supported via --to authjs (writes the
standard @auth/drizzle-adapter user/account tables). Note: Auth.js has no
built-in credential password store, so bcrypt passwords are not carried for the
Auth.js target — OAuth identities migrate, and credential users pair with a
Credentials provider or a one-time reset. For password preservation, use the default
Better Auth target.