Skip to Content
Auth for your appMigrating to Foir

Migrating to Foir

Moving an existing user base to Foir is two decisions: how you bring the accounts over, and how you handle passwords. This guide covers both, then the specifics for Clerk and Better Auth.

The accounts part is straightforward — every provider gives you an export of emails and profile data, and you create a matching customer in Foir for each. Passwords are where migrations differ, so start there.

Passwords: pick an approach

Import your users without their passwords. The first time each one signs in, they use OTP or a password reset to set a new password. Nothing about the old hashes matters, and it works today with no special tooling.

This is the right default for most migrations: it’s the least error-prone, it works regardless of how the old provider hashed passwords, and it quietly retires any weak or stale credentials in the process.

  1. Export your users’ emails and profile fields.
  2. Create a Foir customer for each (see below).
  3. Point your login at Foir. On first visit, users go through OTP or “forgot password” once.

Option B — preserve existing passwords

If a forced reset isn’t acceptable, existing passwords can be preserved when they’re bcrypt hashes. Foir’s identity backend (Ory Kratos) verifies a bcrypt hash on the user’s next login and transparently upgrades it to argon2 — so users keep their current password with no reset.

Note: There is no self-serve importer for external password hashes yet — hash-preserving import is an assisted migration today. If you need it, get in touch before you start. bcrypt is the compatible format; other schemes (for example scrypt) can’t be verified as-is and fall back to Option A.

Bring the accounts over

Create a Foir customer per user through the API. The registration mutation returns tokens, but for a bulk import you typically create the account and let the user establish their own session on first login (Option A).

mutation CustomerRegister($email: String!, $password: String!) { customerRegister(email: $email, password: $password) { customerId email } }

Customer profile and metadata map onto the Foir customer record — see Customers for the data model, including per-customer metadata and the row-level isolation that comes with it.

From Clerk

Clerk’s user export is a CSV that includes each user’s email, profile fields, and their bcrypt password hash.

  1. Export your users from the Clerk dashboard.
  2. Map each row to a Foir customer: email and profile fields → the customer record; Clerk public_metadata → customer metadata.
  3. Passwords: because Clerk exports bcrypt, Option B can preserve them (assisted migration). Otherwise use Option A — import emails only and let users reset on first login.
  4. Repoint your app. Clerk’s <SignIn /> becomes Login with Foir; Clerk session lookups become the Foir customer access token you verify against the JWKS.

From Better Auth

Better Auth keeps users in your own database, so you already have direct access to the user and account tables — no export step, just a query.

  1. Read your user rows (email, name, metadata) and their linked account rows.
  2. Create a Foir customer per user.
  3. Passwords: Better Auth hashes passwords in your database (scrypt by default in current versions), which isn’t bcrypt — so use Option A and have users reset on first login. OAuth-only accounts have no password to migrate at all; the user simply signs in with the same provider through Foir.
  4. Replace Better Auth’s client and server helpers with the Foir flow — hosted Login with Foir for the UI, and the customer access token where you previously read the Better Auth session.

After you migrate

  • Wire lifecycle events. Subscribe to customer.created / updated / deleted so your systems stay in sync. See Webhooks.
  • Choose a session shape. Bearer tokens, or an HttpOnly cookie via a first-party session (BFF).
  • Watch usage. Imported users start counting toward MAU as they sign in.
Last updated on