Migrate from NextAuth / Auth.js
Auth.js is a library, not a hosted service: users live in your database in the standardized
adapter schema (users, accounts, sessions, verification_tokens — same shape for Prisma,
Drizzle, and every other adapter). There is no export step, no vendor, no ticket. And since
Auth.js apps are overwhelmingly OAuth + magic-link (no password hashes to move), the import is
just emails and ids.
1. Read your own tables
Section titled “1. Read your own tables”SELECT u.id, u.email, u.name, a.provider, a."providerAccountId"FROM users uLEFT JOIN accounts a ON a."userId" = u.idWHERE u.email IS NOT NULL;2. Import
Section titled “2. Import”const rows = users.map(u => ({ email: u.email, displayName: u.name || null, sourceUserId: u.id, // your Auth.js user id — the FK your app already uses}));// POST in batches of 500 with source: "nextauth" — loop as in the Clerk guide.See the Clerk guide for the batching loop. If your app’s tables already use
users.id as the FK (they do — that’s the adapter design), keep the sourceUserId → endUserId
map from the response and re-map once.
3. Swap the login surface
Section titled “3. Swap the login surface”Replace the Auth.js routes with the Axowl SDK against your App Group:
- OAuth users (
accounts.provider = "google","github", …) — enable the same providers on the App Group; users click the same button and match by email. - Magic-link users (
verification_tokensflow) — Axowl’s default login is magic link; nothing changes for them. - Then invite everyone to add a passkey — the upgrade Auth.js never gave you, plus org-grade audit sealing on every login. Sessions don’t migrate; users just sign in once.
Gotchas
Section titled “Gotchas”- Custom Credentials providers: if you rolled your own password column next to Auth.js, pass it
as
passwordHashAlgorithm+passwordHash(e.g."bcrypt") — stored dormant like every other provider’s hashes (why). - Users without email (possible with some OAuth providers) can’t be imported — backfill emails
first,
emailis the identity key.