
Research
/Security News
PolinRider Spreads Through Compromised GitHub Accounts and Packagist
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.
@workos/migrations
Advanced tools
A CLI tool for migrating users from identity providers into WorkOS. Supports Auth0, Clerk, Firebase Auth, and custom CSV imports with password hashes, organization memberships, roles, and TOTP MFA factors.
The fastest way to get started is with the interactive wizard:
export WORKOS_SECRET_KEY=sk_...
npx @workos/migrations wizard
The wizard walks you through provider selection, export/transform, validation, and import step by step.
Run directly with npx (no install needed):
npx @workos/migrations <command>
Or install globally:
npm install -g @workos/migrations
workos-migrate <command>
| Command | Description |
|---|---|
wizard | Interactive step-by-step migration wizard |
export-auth0 | Export users from Auth0 via Management API |
export-cognito | Export users + SSO connections from AWS Cognito |
merge-passwords | Merge Auth0 password hashes into the export CSV |
transform-clerk | Transform a Clerk CSV export to WorkOS format |
transform-firebase | Transform a Firebase Auth JSON export to WorkOS format |
validate | Validate a CSV file before import |
import | Import users from CSV into WorkOS |
import-package | Import a migration package directory into WorkOS |
analyze | Analyze import errors and generate retry CSV |
enroll-totp | Enroll TOTP MFA factors for imported users |
process-role-definitions | Create roles and assign permissions in WorkOS |
Run npx @workos/migrations <command> --help for full option details on any command.
WORKOS_SECRET_KEY environment variable)The import CSV uses these columns:
| Column | Required | Description |
|---|---|---|
email | Yes | User email address |
first_name | No | First name |
last_name | No | Last name |
email_verified | No | true or false |
password_hash | No | Password hash value |
password_hash_type | No | bcrypt, firebase-scrypt, ssha, md5 |
external_id | No | External identifier from source system |
metadata | No | JSON string of custom metadata |
org_id | No | WorkOS organization ID |
org_external_id | No | External org identifier (looked up or auto-created) |
org_name | No | Organization name (used with auto-creation) |
role_slugs | No | Comma-separated role slugs for org membership |
The export and transform commands produce CSVs in this format automatically.
Auth0 is a parity-complete migration source. The end-to-end flow is:
export-auth0 --package to produce a migration package with users, organizations, memberships, roles, SSO handoff files, warnings, and the upload-compatible projection. For very large tenants, --engine bulk-job is available; see step 3b.merge-passwords --package <dir> to merge the Auth0 password export into the package. Unsupported hash algorithms are skipped with warnings instead of failing the merge.import-package <dir> to push organizations, users, memberships, roles, and TOTP factors into WorkOS in one shot. SSO connections are surfaced as handoff-only; see docs/auth0-sso-handoff.md.Create a Machine-to-Machine application in Auth0, authorize it for the Management API, and grant these scopes:
read:usersread:user_idp_tokensread:organizationsread:organization_membersread:organization_member_rolesread:rolesread:connectionsread:connections_optionsread:connections_options is required for complete SAML/OIDC handoff exports because Auth0 stores
connection configuration inside the options object. If this scope is missing, later package phases
will warn and omit fields that cannot be read.
workos-migrate export-auth0 \
--domain my-tenant.us.auth0.com \
--client-id <M2M_CLIENT_ID> \
--client-secret <M2M_CLIENT_SECRET> \
--output auth0-export.csv
To write a migration package with users, organizations, memberships, warnings, and skipped-user sidecars:
workos-migrate export-auth0 \
--domain my-tenant.us.auth0.com \
--client-id <M2M_CLIENT_ID> \
--client-secret <M2M_CLIENT_SECRET> \
--package \
--output-dir ./migration-auth0
Package mode also writes workos_upload/users.csv, workos_upload/organizations.csv, and
workos_upload/organization_memberships.csv using the narrower WorkOS upload templates. SSO
connection files remain under sso/ as handoff artifacts.
To include the Auth0 role catalog and per-org role assignments alongside users, organizations, and memberships:
workos-migrate export-auth0 \
--domain my-tenant.us.auth0.com \
--client-id <M2M_CLIENT_ID> \
--client-secret <M2M_CLIENT_SECRET> \
--package \
--entities users,organizations,memberships,roles \
--output-dir ./migration-auth0
This writes role_definitions.csv and user_role_assignments.csv and merges the matched
role_slugs into users.csv and organization_memberships.csv. The
process-role-definitions command can then create the roles in
WorkOS and assign them to memberships. Note that the --use-metadata flow cannot fetch per-org
assignments from Auth0, so it writes the role catalog only and emits a warning.
To write only SSO handoff files:
workos-migrate export-auth0 \
--domain my-tenant.us.auth0.com \
--client-id <M2M_CLIENT_ID> \
--client-secret <M2M_CLIENT_SECRET> \
--package \
--entities sso \
--output-dir ./migration-auth0-sso
Options:
--orgs <ids...> - Filter to specific Auth0 organization IDs--entities <entities> - Comma-separated package entities to export (users,organizations,memberships,roles,sso)--rate-limit <n> - API requests per second (default: 50)--use-metadata - Use user_metadata for org discovery instead of the Organizations API--include-federated-users - Include federated/JIT users in package mode (skipped by default)--include-secrets - Include SSO connection secrets in package handoff files (redacted by default)--job-id <id> - Enable export checkpointing for large tenants--resume [jobId] - Resume a previously checkpointed exportThe export maps Auth0 fields to WorkOS CSV format, including email_verified, external_id, and custom metadata.
Auth0 package SSO export is handoff-only: it inspects Auth0 enterprise strategies for SAML/OIDC configuration and emits only connections with enough reliable handoff data. Database, passwordless, social, generic OAuth, non-SAML/OIDC enterprise, and incomplete connections are skipped with warnings.
For a callback proxy reference implementation during Auth0 enterprise-connection cutover, see proxy-sample-auth0. The repo also includes proxy-sample-cognito for Cognito migrations.
Auth0 does not include password hashes in the Management API export. You need to request a password export from Auth0 support, which provides an NDJSON file. Once you have it, the CLI supports both legacy single-CSV merging and package-aware merging:
# Single CSV (legacy)
workos-migrate merge-passwords \
--csv auth0-export.csv \
--passwords auth0-passwords.ndjson \
--output auth0-with-passwords.csv
# Migration package — updates users.csv, workos_upload/users.csv, and the manifest
workos-migrate merge-passwords \
--package ./migration-auth0 \
--passwords auth0-passwords.ndjson
Package mode warns and omits credentials for users whose hash algorithm is not supported by WorkOS imports (anything other than bcrypt or md5). Users without a matching hash are left without a password and will need to reset on first login.
For tenants where the Management API per-user fetch is too slow, package mode can use Auth0's users-exports job engine instead. This engine returns users without organization membership, so you'll typically run it alongside a Management API run that captured org/membership data, or follow up with a CSV-driven membership reconciliation.
workos-migrate export-auth0 \
--domain my-tenant.us.auth0.com \
--client-id <M2M_CLIENT_ID> \
--client-secret <M2M_CLIENT_SECRET> \
--package \
--engine bulk-job \
--output-dir ./migration-auth0-bulk
A bulk_export_no_org_membership warning is recorded in the package and bulk mode does not populate organizations.csv, organization_memberships.csv, or per-org role assignments.
Continue to Validation, Import, and Post-Import below.
Export your users from the Clerk Dashboard as a CSV file. The export includes columns like id, first_name, last_name, primary_email_address, password_digest, password_hasher, etc.
workos-migrate transform-clerk \
--input clerk-export.csv \
--output clerk-transformed.csv
Options:
--org-mapping <path> - CSV mapping Clerk user IDs to organizations (clerk_user_id,org_external_id,org_name)--role-mapping <path> - CSV mapping Clerk user IDs to roles (clerk_user_id,role_slug)The transformer handles:
Continue to Validation, Import, and Post-Import below.
Export your users from the Firebase Console or using the Firebase CLI (firebase auth:export). This produces a JSON file with a users array.
If you want to migrate passwords, get the hash parameters from Firebase Console > Authentication > Users > Password Hash Parameters. You need the signer key, salt separator, rounds, and memory cost.
workos-migrate transform-firebase \
--input firebase-export.json \
--output firebase-transformed.csv \
--signer-key <BASE64_KEY> \
--salt-separator <BASE64_SEP> \
--rounds 8 \
--memory-cost 14
Options:
--name-split <strategy> - How to split displayName into first/last: first-space (default), last-space, or first-name-only--include-disabled - Include disabled users (excluded by default)--skip-passwords - Skip password hash encoding--org-mapping <path> - CSV mapping Firebase UIDs to organizations (firebase_uid,org_external_id,org_name)--role-mapping <path> - CSV mapping Firebase UIDs to roles (firebase_uid,role_slug)The transformer handles:
$firebase-scrypt$hash=...)displayName splitting into first_name and last_nameContinue to Validation, Import, and Post-Import below.
Configure your AWS credentials using one of the standard methods:
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN)~/.aws/credentials)The Cognito exporter requires these IAM permissions:
cognito-idp:ListUserPoolscognito-idp:ListIdentityProviderscognito-idp:DescribeIdentityProvidercognito-idp:ListUsersworkos-migrate export-cognito \
--region us-east-1 \
--user-pool-ids us-east-1_ABC123,us-east-1_DEF456
Options:
--entities <list> - Comma-separated entities to export: connections, users (default: both)--output-dir <dir> - Output directory for CSV files (default: current directory)--saml-custom-entity-id-template <url> - Template for SAML custom Entity ID (default: urn:amazon:cognito:sp:{user_pool_id})--saml-custom-acs-url-template <url> - Template for SAML custom ACS URL (placeholders: {provider_name}, {user_pool_id}, {region})--oidc-custom-redirect-uri-template <url> - Template for OIDC custom redirect URIThe export produces:
workos_saml_connections.csv - SAML SSO connectionsworkos_oidc_connections.csv - OIDC SSO connectionscustom_attribute_mappings.csv - Supplementary attribute mappingsworkos_users.csv - Users in WorkOS import formatNote: Cognito does not expose password hashes via its API. The password_hash column will be blank for all users. Affected users will need to reset their password post-migration or rely on SSO + JIT provisioning via the migration proxy.
For a seamless cutover where existing IdP configurations continue to work, see the reference proxy implementation in proxy-sample-cognito/. This Lambda handler routes per-tenant traffic between Cognito and WorkOS during the migration window.
Continue to Validation, Import, and Post-Import below.
If you already have a CSV in WorkOS format (see CSV Format above), skip straight to validation:
workos-migrate validate --csv my-users.csv
workos-migrate import --csv my-users.csv
Validate your CSV before importing to catch problems early:
workos-migrate validate --csv users.csv
The validator checks:
email is present and non-empty)The --auto-fix flag corrects common issues automatically:
workos-migrate validate --csv users.csv --auto-fix --output users-fixed.csv
Auto-fix handles whitespace trimming, email lowercasing, and empty field cleanup.
workos-migrate import --csv users.csv
User only (no org membership):
workos-migrate import --csv users.csv
Single org (all users into one organization):
workos-migrate import --csv users.csv --org-id org_01ABC
Multi-org (org per row, from CSV columns):
workos-migrate import --csv users.csv --create-org-if-missing
In multi-org mode, the importer reads org_id, org_external_id, or org_name from each row. Organizations are cached in memory to avoid repeated API lookups. If --create-org-if-missing is set, organizations referenced by name or external ID that don't exist in WorkOS are created automatically.
workos-migrate import \
--csv users.csv \
--concurrency 20 \
--rate-limit 50 \
--workers 4 \
--chunk-size 5000 \
--job-id my-migration
--concurrency <n> - Parallel API requests per worker (default: 10)--rate-limit <n> - Max requests per second across all workers (default: 50)--workers <n> - Worker threads for CPU distribution (default: 1, requires --job-id)--chunk-size <n> - Rows per checkpoint chunk (default: 1000)For large migrations, use --job-id to enable checkpointing. If the process crashes or is interrupted, resume from where it left off:
# Start with checkpointing
workos-migrate import --csv users.csv --job-id my-migration
# Resume after interruption
workos-migrate import --csv users.csv --resume my-migration
Checkpoint state is stored in .workos-checkpoints/<job-id>/.
Preview what the import would do without making any API calls:
workos-migrate import --csv users.csv --dry-run
Import errors are written to a JSONL file (default: errors.jsonl). Each line contains the email, error type, HTTP status, and message for a single failure.
After an import, analyze errors to understand what went wrong and generate a retry CSV:
workos-migrate analyze \
--errors errors.jsonl \
--retry-csv retry.csv \
--original-csv users.csv
The analyzer groups errors by pattern, classifies them as retryable or non-retryable, and suggests fixes. The retry CSV contains only the rows that failed with retryable errors, so you can re-import just those users.
Migration packages produced by export-auth0 --package (and other providers as they move onto the package contract) can be imported in one step with import-package:
# Plan only — print what would happen and exit
workos-migrate import-package ./migration-auth0 --plan
# Dry run — validate the package and write workos_import_summary.json with status=planned
workos-migrate import-package ./migration-auth0 --dry-run
# Live import
workos-migrate import-package ./migration-auth0
The orchestrator runs entities in this order:
--create-org-if-missing semantics).runImport on users.csv).process-role-definitions on role_definitions.csv).user_role_assignments.csv).enroll-totp on totp_secrets.csv).sso/handoff_notes.md in the package for next steps.Every run writes workos_import_summary.json (or --summary <path>) with per-entity status, totals, succeeded/failed counts, and warnings. Per-row errors land in workos_import_errors.jsonl (or --errors <path>).
If your source system has TOTP secrets (e.g., from Auth0 or Clerk), you can enroll them in WorkOS after import:
workos-migrate enroll-totp \
--input totp-secrets.csv \
--totp-issuer "MyApp"
The input file can be CSV (email,totp_secret) or NDJSON (one JSON object per line with email and totp_secret or mfa_factors fields). Format is auto-detected from the file extension.
Options:
--format <csv|ndjson> - Override auto-detection--concurrency <n> - Parallel requests (default: 5)--rate-limit <n> - Requests per second (default: 10)--totp-issuer <name> - Issuer shown in authenticator apps--dry-run - Validate without enrollingCreate roles and permissions in WorkOS from a CSV, then assign them to users:
# Create roles and permissions
workos-migrate process-role-definitions \
--definitions role-definitions.csv
# Create roles and assign to users
workos-migrate process-role-definitions \
--definitions role-definitions.csv \
--user-mapping user-roles.csv \
--org-id org_01ABC
Role definitions CSV (role_slug,role_name,role_type,permissions[,org_id]):
role_slug,role_name,role_type,permissions
admin,Administrator,environment,"read,write,delete"
viewer,Viewer,environment,read
org-admin,Org Admin,organization,"read,write",org_01ABC
User-role mapping CSV (email,role_slug):
email,role_slug
alice@example.com,admin
bob@example.com,viewer
npm install
npm run build
npm run dev # Run with tsx (no build step)
npm run lint
npm run typecheck
npm test # 120 tests across 10 suites
See CONTRIBUTING.md for development setup and guidelines. Security issues should be reported privately as described in SECURITY.md.
MIT. See LICENSE.
FAQs
CLI tool to migrate data from various identity providers to WorkOS
The npm package @workos/migrations receives a total of 5,056 weekly downloads. As such, @workos/migrations popularity was classified as popular.
We found that @workos/migrations demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 9 open source maintainers collaborating on the project.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.

Company News
Allow myself to introduce... myself.