All releases
Feature

Import bcrypt password hashes — migrate from Auth0, PHP and Rails without a password reset

The bulk end-user import now accepts bcrypt password hashes alongside argon2id. Users you bring over from Auth0, PHP, Spring or node bcrypt sign in with the password they already have, and Auth upgrades the credential to argon2id on that first signin.

POST /v1/apps/:appId/end-users/import takes NDJSON rows with password_hash and password_hash_algorithm. Until now only argon2id was accepted, which meant every user migrating off a bcrypt-based stack went through a password reset before they could sign in. Rows tagged bcrypt are now stored as they are — $2a$, $2b$ and $2y$ hashes, cost 4 to 10 — and verified the first time the user signs in.

That first successful signin also replaces the bcrypt hash with an argon2id hash of the same password, in the same request. The user sees a normal signin; the credential they leave behind is the one every other Auth account has. The upgrade is recorded on the app's audit log as auth.password.upgraded, so you can watch a migration complete from GET /v1/apps/:appId/audit-logs?action=auth.password.upgraded.

Any bcrypt cost works. Auth answers every signin attempt in the same time whether the identifier exists or not, so an attacker cannot list your users by timing the form; a bcrypt verify at cost 10 or below is padded up to that floor. Costs 11 and above — the Devise, Rails, Laravel 11 and Django defaults — are slower than the floor and are accepted as they are: a migrated account answers a little later than others until its first signin re-hashes it, a small and temporary difference we chose to accept so nobody has to reset a password. See the migration guide.