Keycloak → Auth0
Export the Keycloak realm with the CLI, then load profiles into an Auth0 database connection with the bulk import job. Map a password only when the hash algorithm is one Auth0 documents.
Should you make this move?
Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.
Keycloak
- Self-hosted realms, clients, and login flows keep identity on infrastructure you operate
- Centralized sign-in, federation, MFA, and user lifecycle controls reduce application security work
- Realm export, password algorithms, and custom providers make a cutover an operations project
- Passwords, sessions, factors, identities, and authorization policy are unusually sensitive to migration errors
Auth0
- Mature federation, extensibility, enterprise connections, and migration tooling cover complex identity programs
- Centralized sign-in, federation, MFA, and user lifecycle controls reduce application security work
- Tenant configuration, actions, pricing, and Auth0-specific claims can create substantial lock-in
- Passwords, sessions, factors, identities, and authorization policy are unusually sensitive to migration errors
Auth0: Mature federation, extensibility, enterprise connections, and migration tooling cover complex identity programs. This removes a major source-side concern: Realm export, password algorithms, and custom providers make a cutover an operations project.
What you lose: Self-hosted realms, clients, and login flows keep identity on infrastructure you operate. What you inherit: Tenant configuration, actions, pricing, and Auth0-specific claims can create substantial lock-in.
Jump to a section
Know the shape of the move.
This timeline assumes
- You can stop Keycloak nodes and run kc.sh export. The Admin Console export cannot export users and masks secrets.
- The Auth0 destination is a database connection, which is what the bulk import job fills.
- You can split the import. Auth0's file limit is 500KB, and their guideline is about 1,000 users with fewer than 10 metadata fields.
- You will read the credential algorithm in the export before you put it in custom_password_hash.
- Applications move one at a time, and Keycloak stays up until Auth0 logins are proven.
What survives the move.
“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.
| Item | Outcome | Impact | What happens | Mitigation |
|---|---|---|---|---|
| Realm users | partial | critical | The CLI can write users to separate files, one file, or the realm file. The default is different files, 50 users each. Over 50,000 users, Keycloak tells you to export a directory, not one file. Consistency wants the nodes stopped. | Stop the nodes, export the realm you mean, and count users in the files. |
| Admin Console partial export | lost | high | That path does not include users, masks passwords and client secrets, and Keycloak says it is not for moving servers. | Do not build the import from realm-export.json downloaded in the browser. |
| Password credentials | partial | critical | Auth0's schema accepts password_hash for bcrypt, or custom_password_hash for algorithms they list, including bcrypt, argon2, pbkdf2, scrypt, hmac, ldap, and several digests. A hash they do not list means the user resets. | Read the algorithm and parameters in the Keycloak credential. Import one known password before the batch. |
| Sessions, events, and revoked tokens | lost | high | Keycloak excludes them from the export. Auth0 sessions start fresh. | Tell people they will sign in again. |
| Import file shape | partial | high | Auth0 wants a JSON array, not the NDJSON shape of an Auth0 export, and not an unbounded Keycloak file. Each job is capped at 500KB. Job data is deleted after 24 hours. A job that does not finish in time is failed. | Convert and split. Download job errors the same day. |
| User ids | partial | high | Auth0 says bulk import adds an auth0| prefix to imported user ids. Keep the Keycloak id in metadata even when you also pass a user_id, so a later export does not confuse you. | Store the Keycloak id in app_metadata. Reconcile a sample. |
| Clients and identity providers | manual | critical | A realm export contains clients and providers, but Auth0 applications and connections are configured in Auth0, not by posting that JSON. | Recreate each application and connection. Test the callback. |
| Authentication flows and required actions | manual | critical | Keycloak flows, SPIs, and required actions do not run inside Auth0. Actions are the Auth0-side rewrite. | List every flow that blocks a login or adds a claim. Rewrite it and compare tokens. |
| MFA credentials | manual | high | A Keycloak OTP or WebAuthn credential is not automatically an Auth0 factor. Auth0's import schema has an mfa_factors object for secrets you format yourself. | Enroll a test user. If the secret format does not match the schema, plan a fresh enrollment. |
| Groups and roles | partial | high | You can put role names into Auth0 metadata or roles, but Keycloak composite roles and group trees need a map. | Flatten the roles the applications check. Test one user per role. |
Where each thing goes.
| Source | Destination | Method | Notes |
|---|---|---|---|
| Keycloak user id | app_metadata keycloak_user_id | transform | Keep it even if Auth0 assigns auth0|... |
| username and email | email and username fields in the import JSON | transform | Resolve duplicates before the job. |
| emailVerified | email_verified | transform | Do not mark a contact verified if Keycloak had not. |
| enabled false | blocked true | transform | Disabled users should not become active Auth0 users. |
| firstName and lastName | given_name and family_name | transform | Map empty names on purpose. |
| Credential hash | password_hash or custom_password_hash | manual | Only if the algorithm is on Auth0's list. |
| Realm role | Auth0 role or metadata | manual | Flatten composites first. |
| Identity provider link | Auth0 connection | manual | Create the connection, then test a login. |
| Client | Auth0 application | manual | New client id, secret, and callbacks. |
Make the move recoverable.
Create the source-of-truth backup
Produce a CLI export while Keycloak is still the system of record.
- Stop Keycloak nodes, as the export guide recommends for a consistent file.
- Run kc.sh export for the one realm, to a directory if you have many users.
- Copy the realm file and the user files off the server.
- Start Keycloak again only after the copy is stored.
Proof to capture: The user-file count matches the realm's user count, and the files are named the way Keycloak's import rules expect.
Auth0 import batches
Fit Keycloak users into Auth0's 500KB JSON jobs.
- Read users from the CLI files.
- Emit JSON arrays under 500KB.
- Put the Keycloak id in app_metadata.
- Omit custom_password_hash unless the algorithm and parameters match the schema.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
One-user password proof
See if Auth0 will verify the hash you mapped.
- Import one user whose password you know.
- Sign in against the Auth0 database connection.
- If Auth0 rejects the hash, drop custom_password_hash for the rest and send resets.
- Only then start the remaining jobs.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
The things most likely to hurt.
These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.
The browser export is used as the user source
The file has no users, and secrets are asterisks.
- Consequence
- Auth0 gets an empty or useless import.
- Mitigation
- Export with the CLI while nodes are stopped.
Stop if: The user files are missing or secrets are masked.
An unlisted hash is sent as custom_password_hash
The job fails, or users are created and cannot sign in.
- Consequence
- You discover it at cutover.
- Mitigation
- Test one password. If it fails, import profiles and reset.
Stop if: The known user cannot sign in.
A Keycloak flow has no Auth0 Action
The token is missing a claim the API still checks.
- Consequence
- Requests that used to pass now fail, or the reverse.
- Mitigation
- Compare tokens for each role before you switch the app.
Stop if: A required claim differs.
Do the work in this order.
- Days 1 to 3 · inventory
List realm pieces
6 to 10 hours active2 to 3 days elapsedApp owners waiting- List clients, identity providers, flows, and roles the apps check.
- Pick the Auth0 database connection.
Depends on: Keycloak admin, Auth0 admin
Stop / go checkpointExport?
Go when: Each application has an owner.
Stop when: A production client is unowned.
- Days 3 to 5 · backup
CLI export
3 to 8 hours active1 to 2 days elapsedNode stop waiting- Stop nodes and export the realm.
- Count users in the files.
Depends on: Approved inventory
Stop / go checkpointConvert?
Go when: The count matches and file names are valid.
Stop when: The console file was used, or the count is short.
- Days 5 to 10 · pilot
One-user import
8 to 20 hours active3 to 6 days elapsedAuth0 job waiting- Convert a tiny JSON file.
- Import one known user and sign in.
- Rewrite one claim as an Action.
Depends on: Verified export
Stop / go checkpointImport everyone?
Go when: The test login works or the reset path is accepted.
Stop when: The hash test fails and no reset plan exists.
- Days 10 to 15 · cutover
Import and switch one app
5 to 14 hours active2 to 5 days elapsedImport jobs waiting- Run the remaining jobs.
- Switch one application to Auth0.
- Keep Keycloak serving the others.
Depends on: Passed pilot
Stop / go checkpointSwitch the next app?
Go when: The app accepts Auth0 tokens and the user count matches.
Stop when: Job errors are unresolved.
- Days 15 to 20 · observe
Watch sign-ins
2 to 8 hours active3 to 5 days elapsedLogin cycle waiting- Review failed logins.
- Keep the Keycloak export.
Depends on: Verified cutover
Stop / go checkpointRetire Keycloak?
Go when: No rollback trigger fires.
Stop when: A connection or claim fails.
Cut over with a way back.
Cutover
Have Auth0 authenticate the application you are switching, with Keycloak still intact.
- Freeze Keycloak profile edits or take a final CLI export.
- Run the remaining import jobs and read every error.
- Repeat the known-user login.
- Point one application at the Auth0 issuer.
- Leave Keycloak running for everything else.
Proof to capture: The Auth0 user count matches the export, and the switched app accepts an Auth0 login.
Rollback
Point the application back at Keycloak.
- Stop Auth0-only signups if you can.
- Restore the application's Keycloak client settings.
- List users who exist only in Auth0.
- Import those profiles back with a tested path, or force a reset. Do not assume the Auth0 hash is a Keycloak hash.
- Confirm the app accepts Keycloak tokens.
Proof to capture: The application uses Keycloak again, and Auth0-only users are listed.
- The known-user login fails
- Import jobs leave unresolved errors
- A required claim is missing
- An identity provider login fails
Prove the migration worked.
Every blocking check must pass. Capture the evidence before cleanup begins.
| Pass | ID | Check | Method | Expected result | Evidence |
|---|---|---|---|---|---|
V-01Blocking | User count | Count users in the CLI files and in Keycloak. | The counts match. | Export manifest | |
V-02Blocking | Import errors | Read each Auth0 job result before it ages out. | Every failed row is fixed or accepted. | Job log | |
V-03Blocking | Known user | Sign in with a password you know. | Auth0 accepts it, or the reset flow works. | Login note | |
V-04Blocking | Keycloak id | Look up app_metadata on a sample. | The old id is present. | Id sample | |
V-05Blocking | Claims | Compare Auth0 tokens with what the app checked under Keycloak. | Required claims match. | Token sample | |
V-06Blocking | Providers | Sign in with each social or enterprise connection. | The user lands on the right account. | Provider checklist | |
V-07Blocking | Authorization | Call the app as each role. | Access matches the old checks. | Role matrix | |
V-08Blocking | Reverse one app | Point a test app back at Keycloak. | Keycloak tokens work again. | Rollback note |
Remove the scaffolding safely.
Safe after: Production apps have signed in through Auth0 and the CLI export is archived.
- Store the CLI export with restricted access. It can contain credential material.
- Delete pilot Auth0 users and import files from laptops.
- Rotate secrets that landed in tickets.
- Keep Keycloak until the rollback date.
- Write down the hash decision you made, so a later import does not guess.
Verify against the primary material.
Platform behavior changes. Check these sources and the review dates above before executing a production migration.
- Keycloak: importing and exporting realmsAccessed 2026-11-05
- Auth0: bulk import user dataAccessed 2026-11-05
- Auth0: bulk import user schemaAccessed 2026-11-05