Learn how to export and import users from your own data store.
The WorkOS AuthKit API allows you to migrate your existing user data from a variety of sources. In this guide, we’ll walk through the steps to export, and then import users from your own data store.
While moving authentication related metadata to WorkOS, most applications will continue to store certain user information in their data store. This common subset of data will usually be the following:
| Field | Description | Status |
|---|---|---|
| The user’s email address. Used for various authentication and verification purposes. | Required | |
| First Name | The user’s first, or given name. | Optional |
| Last Name | The user’s last, or family name. | Optional |
| Verification Status | The user’s email verification status if they have gone through a verification flow. Assumed as “not verified” unless supplied. | Optional |
| Password | The user’s password hash, if they use password-based authentication. | Optional |
While preparing the migration, you’ll want to ensure this information is programmatically available for use in the import step, this can mean:
After the data is accessible, we can configure the import.
Now that the User data is available, we can import it into WorkOS.
If you have webhook endpoints configured, temporarily disable delivery for the duration of the bulk import to avoid overwhelming your webhook consumers with high volumes of user.created, organization.created, and organization_membership.created events.
You can disable a webhook endpoint using the Update Webhook Endpoint API:
curl -X PATCH https://api.workos.com/webhook_endpoints/{id} \ -H "Authorization: Bearer sk_example_123456789" \ -H "Content-Type: application/json" \ -d '{ "status": "disabled" }'
Re-enable the endpoint after the import is complete by setting status back to "enabled".
The WorkOS CLI includes a migrations tool that can import users from a CSV file:
npx workos migrations import --csv users.csv
For a guided, interactive experience:
npx workos migrations wizard
The CLI handles batching and rate limiting automatically.
For each of your users, you can call the WorkOS Create User API. This will create a matching User object within WorkOS.
A successful response will include a new WorkOS user ID, most apps will want to persist this WorkOS user ID alongside the application-local user object.
{ "object": "user", "id": "user_01E4ZCR3C56J083X43JQXF3JK5", "email": "marcelina.davis@gmail.com", "firstName": "Marcelina", "lastName": "Davis", "emailVerified": true, "createdAt": "2021-06-25T19:07:33.155Z", "updatedAt": "2021-06-25T19:07:33.155Z" }
There are now several options on how to proceed, depending on your application’s needs:
If your users currently use password-based authentication, you can import existing password hashes during the users creation process, or later using the WorkOS Update User API.
WorkOS currently supports the following password hashing algorithms:
bcryptscryptfirebase-scryptsshapbkdf2argon2For scrypt and pbkdf2 passwords, use the PHC string format.
The hash and salt should be B64 encoded: trim the = characters that represent Base64 padding. Using a PHC-formatting library, like
Node’s @phc/format, should handle this for you.
The following table shows how to map the scrypt and pbkdf2 parameters to the PHC parameters.
Scrypt value | PHC hash parameter | |
|---|---|---|
key length | → | kl |
cost | → | n |
rounds | → | r |
parallelization | → | p |
A valid scrypt PHC formatted string looks like this:
$scrypt$v=1$n=16384,r=8,p=1,kl=64$Swhqd4iUYTtWfbCYIPeuMw$q7pfdBQMJujd5FX/qX+ozM2O6aNqP+mo1ZnHGH15XM2vlhroQfPA037UpbdfpH4H66OrSPjsUhfkAMuNoBiQvw
pbkdf2 value | PHC hash parameter | |
|---|---|---|
digest | → | d |
iterations | → | i |
For pbkdf2 allowed values for digest are sha256 or sha512. The value for iterations is dependent on digest. For sha256 there is a minimum of 600,000 iterations and a max of 1,000,000. For sha512 there is a minimum of 210,000 and a max of 1,000,000.
A valid pbkdf2 PHC formatted string looks like this:
$pbkdf2$i=600000,d=sha256$T2ptRFh6MXhDQVh2SWZuUGdpQXBUTg$xXiyTisD7390NijyCv5ICMhFW4eDuMlzypRoLGLyIvA
argon2 value | PHC hash parameter | |
|---|---|---|
variant | → | algorithm id |
version | → | v |
memory | → | m |
time | → | t |
parallelism | → | p |
The variant should be argon2id, but older supported variants include argon2d and argon2i. The version must be 19. The following memory, time (iterations), and parallelism settings are based on OWASP recommendations. Memory is specified in KiB with a minimum of 4,096 KiB (4 MiB) and maximum of 262,144 KiB (256 MiB). For time, there is a minimum of 1 iteration and a maximum of 5 iterations, except for argon2i which has a minimum of 3 iterations. Parallelism ranges from 1 to 8 threads. If your requirements fall outside of these guidelines, please contact support.
A valid argon2 PHC formatted string looks like this:
$argon2id$v=19$m=65536,t=3,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG
For firebase-scrypt passwords, refer to the Firebase Migration guide for an example of how to format the password_hash.
For ssha passwords, use the following algorithm:
salt: random bytessalt using the SHA1 algorithm{SSHA}A high-level representation is: {SSHA}base64(sha1(password + salt) + salt).
Once imported, users can continue to sign-in with their existing password, without having to go through a password reset flow.
If you are unable to export passwords from your existing data store, whether for security reasons or other limitations, you can programmatically trigger a password reset flow using the WorkOS Password Reset API.
This process can be initiated at any time, and doesn’t need to happen during the user import process.
Some applications may want to remove password-based authentication when switching to WorkOS, in favor of another method like Magic Auth. If this is the case for your application, you can skip dealing with passwords entirely.
If you have users who previously signed in using social auth providers, such as Google or Microsoft, those users can continue to sign in with those providers after you’ve migrated to WorkOS.
Check out our integrations page for guidance on configuring the relevant provider’s client credentials in WorkOS.
After your provider is configured in WorkOS, users can sign in with their provider credentials and will be automatically linked to a WorkOS user. WorkOS uses the email address from the social auth provider to determine this match.
Email verification behavior varies depending on whether the provider is known to verify email addresses. For example, users signing in using Google OAuth and a gmail.com email domain will not need to perform the extra verification step.
If your application uses enterprise SAML or OIDC connections for SSO, you can migrate them to WorkOS. The best strategy depends on how many connections you have and whether you control the existing SSO infrastructure.
For smaller deployments, the simplest path is to recreate each connection in WorkOS and coordinate with each customer’s IT team to update their Identity Provider configuration. You can use the Admin Portal to let customers self-service their SSO setup, reducing the coordination burden.
For each connection:
connection.activated webhook to roll out the customer to the WorkOS connectionFor larger deployments, coordinating with every customer’s IT team is impractical. Instead, you can perform a transparent migration so that IT admins do not need to reconfigure anything on their end. The approach depends on whether you are migrating from an external identity service or from a homegrown SSO implementation.
If you are migrating from an external identity provider (such as Auth0, or another third-party service), you can place a proxy in front of the service’s custom domain to route IdP callback traffic to WorkOS.
This approach requires that you have a custom domain configured with your current provider (e.g., auth.your-domain.com) so that you control the domain routing.
Once connections are imported into WorkOS, configure a proxy in front of your custom domain to route IdP callback traffic to WorkOS. The proxy intercepts callbacks at your custom domain’s callback path and redirects them to your WorkOS custom domain.
The proxy flow works as follows:
fallback query parameter, and the proxy forwards the original callback to your existing providerThis fallback mechanism ensures zero downtime – connections that haven’t been migrated yet continue to work through your existing provider.
The proxy can be implemented using Cloudflare redirect and transform rules, a Cloudflare Worker, or any reverse proxy you control. Contact support@workos.com for a reference implementation and import assistance.
If your application has its own SSO implementation where you directly handle IdP callbacks, you already control the callback endpoints. Instead of a proxy, you can modify your existing callback handlers to forward IdP responses to WorkOS using a feature flag mechanism.
The WorkOS migrations CLI can generate a CSV template for your connection data:
npx workos migrations wizard
Select “Generate CSV template” and choose the Connections template. This produces a CSV with the following columns:
| Column | Required | Description |
|---|---|---|
organizationName | Yes | Name of the organization |
organizationId | Yes | Your existing organization identifier |
domains | No | Semicolon-separated list of domains (e.g., acme.com;app.acme.com) |
idpEntityId | No | Identity Provider Entity ID |
idpUrl | No | Identity Provider SSO URL |
x509Cert | No | IdP X.509 signing certificate |
idpIdAttribute | No | Attribute used for user identification (e.g., email) |
idpMetadataUrl | No | IdP metadata URL |
Fill in the template with your existing connection configurations and send it to support@workos.com for import.
This gives you per-organization control over the migration without requiring IdP reconfiguration.
import { WorkOS } from '@workos-inc/node'; const workos = new WorkOS(process.env.WORKOS_API_KEY); const featureFlags = workos.featureFlags.createRuntimeClient(); await featureFlags.waitUntilReady({ timeoutMs: 5000 }); async function handleSSOCallback(req: Request): Promise<Response> { const organizationId = extractOrganizationFromCallback(req); const useWorkOS = featureFlags.isEnabled('workos-sso-enabled', { organizationId, }); if (useWorkOS) { // Forward the IdP response to WorkOS for processing const { user } = await workos.userManagement.authenticateWithCode({ code: req.query.code, clientId: process.env.WORKOS_CLIENT_ID!, }); return handleWorkOSUser(user); } // Continue with existing SSO flow for non-migrated organizations return handleExistingSSOCallback(req); }
Regardless of which route you use, the authorization URL piece works the same way. You can use WorkOS Feature Flags to control which organizations use WorkOS SSO versus your existing provider during the migration. Create a feature flag (e.g., workos-sso-enabled) and target it to specific organizations as you migrate them.
import { WorkOS } from '@workos-inc/node'; const workos = new WorkOS(process.env.WORKOS_API_KEY); const featureFlags = workos.featureFlags.createRuntimeClient(); await featureFlags.waitUntilReady({ timeoutMs: 5000 }); function getSSOAuthorizationUrl(organizationId: string): string { const useWorkOS = featureFlags.isEnabled('workos-sso-enabled', { organizationId, }); if (useWorkOS) { return workos.userManagement.getAuthorizationUrl({ provider: 'authkit', organizationId, clientId: process.env.WORKOS_CLIENT_ID!, redirectUri: process.env.WORKOS_REDIRECT_URI!, }); } // Fall back to existing SSO provider for organizations not yet migrated return getExistingSSOAuthorizationUrl(organizationId); }
This lets you gradually migrate organizations to WorkOS SSO, verify each one works correctly, and roll back individual organizations if needed – all without code deployments.
Many applications allow users to sign up at any time. If your app offers this feature, then you should consider the timing of your migration. If any users sign up after you’ve completed importing users into WorkOS, but before you’ve switched to WorkOS for authentication, then those users will have been omitted from the migration process.
There are two main strategies to handle this:
The simplest solution is to schedule an appropriate time for the migration and disable signup while in progress. This may be done using temporary code added to your application and controlled by a feature flagging system.
After the migration is complete, your application should be updated to perform authentication using WorkOS, and the signup flag block disabled. This helps to ensure the export/import process captures all active users.
For applications that want to avoid disabling signups, a “dual-write” strategy can be used.

When a new user signs-up, in addition to creating a user record in the existing user store, the application should also create a matching record in WorkOS using the Create User API. As time passes, WorkOS will stay consistent with future new users, but a migration will still need to be performed for the historical set of users.
You will need to perform the same export and import process into WorkOS, but keeping in mind that some users will already exist in WorkOS as a result from the “dual-write”.
While this minimizes forms of downtime for your application, there are other complications. For example, if a user updates their email or authentication method, you will need to perform the same update in WorkOS, at least until the migration process is complete.
Your timeline for completing the migration, along with your user’s tolerances for disruption, will affect which strategy makes more sense for your application.
Disabling signups, or even sign-in entirely, and doing a “big-bang” migration by moving all users at the same time, could be reasonable for a smaller application. However, larger applications that are on the critical path for their customers may need a more careful path in order to provide consistent access.
User management migration complexity can vary, so it is important to consider how existing application constraints will transfer to WorkOS. If you have any questions, reach out to support@workos.com or via your team’s WorkOS Slack channel for more help planning your migration.