Export users from WorkOS
Learn how to export your users and their password hashes from WorkOS.
If you’re moving your users from WorkOS to another system, you can take their password hashes with you, so they can keep signing in with their existing passwords. In this guide, we’ll walk through how to request an export of your users from WorkOS, what the export contains, and how your new system can verify each password hash format.
WorkOS deliberately doesn’t offer an API or dashboard export of password hashes. Password hashes are sensitive credentials, so for security reasons they’re only released through a request to WorkOS support and delivered securely.
To request an export, email support@workos.com and include:
- The environments you need exported, such as Production or Staging.
- A secure email address to receive the export.
If you only need your users’ profile data, you can export it yourself from the WorkOS Dashboard. Go to Users in the environment you want to export, and click the download icon (Export CSV) above the users table. Under Users created, choose All time to include every user, then click Export CSV. When the export is ready, click Download. Any search or filters applied to the table narrow the export to the matching users.
This CSV includes profile fields such as email, name and email verification status. It never includes password hashes.
WorkOS sends the export as a CSV file through a secure file-transfer service, to the email address you provided.
The CSV has the following columns:
| Column | Description |
|---|---|
email |
The user’s email address. |
password_hash |
The user’s password hash, in one of the formats below. |
first_name |
The user’s first, or given name. |
last_name |
The user’s last, or family name. |
Parse the file with a CSV library rather than splitting lines on commas: some password hashes contain commas in their parameters.
Users who have never set a password, such as those who only sign in with SSO, a social provider or Magic Auth, have no password hash to export.
A user’s password hash is stored in the format it was set or imported in. Most can be verified directly with a standard library for the algorithm. Imported ssha, ssha256 and some pbkdf2 hashes were wrapped in bcrypt at import, so your new system needs to verify them as described below.
password_hash starts with |
Where the password came from | Verify with |
|---|---|---|
$2b$10$ |
Set in WorkOS | bcrypt |
$2a$, $2b$ or $2y$ |
Imported as bcrypt |
bcrypt |
$scrypt$, $firebase-scrypt$, $argon2id$ or $pbkdf2$ |
Imported as scrypt, firebase-scrypt, argon2 or pbkdf2 |
The original algorithm |
$bcrypt-pbkdf2-sha1$ or $bcrypt-pbkdf2-sha256$ |
Imported as pbkdf2, then re-hashed with bcrypt |
bcrypt over the hex-encoded PBKDF2 key |
$bcrypt-ssha$ or $bcrypt-ssha256$ |
Imported as ssha or ssha256 |
bcrypt over a hex-encoded SHA1 or SHA256 digest |
The examples in this section are illustrative and don’t correspond to real passwords.
Passwords that users set in WorkOS, for example when signing up or resetting their password, are hashed with bcrypt at cost 10 and stored in modular crypt format: $2b$10$<salt><digest>.
Imported bcrypt hashes are kept exactly as you provided them, so they may use the $2a$, $2b$ or $2y$ prefix and a cost other than 10.
Any bcrypt library can verify these. If yours doesn’t accept the $2y$ prefix, replace it with $2b$; the two are interchangeable for verification.
Passwords imported with scrypt, firebase-scrypt, argon2 or pbkdf2 stay as the PHC strings you imported: $<id>$<params>$<base64 salt>$<base64 digest>, with a v= version segment after the ID for some algorithms. Argon2 hashes may use the argon2id, argon2i or argon2d ID. Salts and digests are base64 encoded without padding.
The parameters are the ones described in Importing passwords. For firebase-scrypt, the string also carries your Firebase project’s signer key and salt separator in the sk and ss parameters, as described in the Firebase migration guide.
pbkdf2 hashes that use the sha1 digest, or the sha256 digest below the minimum iteration count, were re-hashed with bcrypt at import. They’re stored as PHC strings with the ID bcrypt-pbkdf2-sha1 or bcrypt-pbkdf2-sha256:
- The salt field is the original PBKDF2 salt.
- The
iparameter is the original iteration count, andklis the derived key length in bytes. - The hash field is a complete bcrypt hash (
$2b$10$…), base64 encoded.
To verify a password, derive the key with PBKDF2 using the digest named in the ID, the decoded salt, i iterations and a key length of kl. Encode the derived key as lowercase hex, then check that hex string against the decoded bcrypt hash with a bcrypt library.
Imported ssha and ssha256 hashes were wrapped in bcrypt at import. They’re stored as PHC strings with the ID bcrypt-ssha or bcrypt-ssha256:
- The salt field is the original
sshaorssha256salt. - The
spparameter records the salt position forbcrypt-ssha256.sp=prefixmeans the salt comes before the password. Ifspis absent, the salt is a suffix and comes after the password.bcrypt-sshahashes never havesp; their salt is always a suffix. - The hash field is a complete bcrypt hash (
$2b$10$…), base64 encoded.
These are not plain ssha or ssha256 hashes, and they can’t be converted back to that format. Your new system must verify them as bcrypt over the hex-encoded SHA1 or SHA256 digest, with the salt position following sp:
| ID | sp |
Verify as |
|---|---|---|
bcrypt-ssha |
Absent | bcrypt(hex(sha1(password + salt))) |
bcrypt-ssha256 |
Absent | bcrypt(hex(sha256(password + salt))) |
bcrypt-ssha256 |
prefix |
bcrypt(hex(sha256(salt + password))) |
To verify a password:
- Base64-decode the salt and hash fields of the PHC string.
- Compute the SHA1 or SHA256 digest of the
UTF-8encoded password and the raw salt bytes, in the order set bysp. - Encode the digest as lowercase hex.
- Check that hex string against the decoded bcrypt hash with a bcrypt library.
If your new system can’t run custom verification like this, these users will need to reset their passwords.
If your new system can’t verify one of these formats, or you’d rather not move password hashes, those users can reset their passwords instead. While your users still sign in with WorkOS, you can trigger a reset with the WorkOS Password Reset API. A password reset in WorkOS stores a standard $2b$10$ bcrypt hash, which you can then include in a new export. After you switch, use your new system’s password reset flow.
If you have any questions about exporting your users, reach out to support@workos.com.