User migration
Bring users and configuration from Keycloak, Auth0, AWS Cognito, Okta or a CSV file, keeping passwords wherever the source exports their hashes.
Authio imports from a file: the export your current provider gives you, or a CSV or JSON list you produce yourself. Every import is rehearsed first — nothing is written until you have read a report of what would happen — and every row goes through the same checks, plan quotas and permissions as a change made in the console. Rows that already exist are skipped, so running the same file again is safe.
Passwords come across as the hashes your old system stored. Authio verifies such a hash in its original format the first time the person signs in, then replaces it with a native one, so nobody has to reset a password that the source let you export. Authio never calls your old provider: whatever is not in the file has to be recreated.
Choose a path
| Path | Use it when | Where |
|---|---|---|
| Migrate from a provider | You have an export from Keycloak, Auth0, AWS Cognito or Okta. It can carry realms, roles, clients and identity providers along with the users. | Migration, with the realm:write permission |
| Import a file of users | You have a list of people from any system, as CSV or JSON. | Users › Import from file, with the client:write permission |
The other main lists — realms, roles, scopes, clients, identity providers, consumers, API secrets, signing keys and webhooks — have the same Import from file action, with the same rehearse-then-apply flow.
Supported sources
| Source | File to upload | Passwords |
|---|---|---|
| Keycloak | A realm export with users, as JSON: kc.sh export --realm <name> --users realm_file. | PBKDF2 (SHA-256 or SHA-512) and Argon2id credentials migrate without a reset. PBKDF2 over SHA-1, and Argon2 credentials exported without their parameters, need a reset. |
| Auth0 | The output of a bulk user export job, as newline-delimited JSON. | bcrypt hashes migrate without a reset when the file carries them. The ordinary export job leaves them out: ask Auth0 to release the hashes. |
| AWS Cognito | The user pool exported to CSV. | Cognito exports no password material, so everyone sets a new password. Attributes, confirmed addresses and phone numbers come across. |
| Okta | The JSON returned by GET /api/v1/users, saved across all its pages. Okta has no export file. | Not in that response, so everyone sets a new password. Only accounts Okta lists as ACTIVE arrive with a confirmed address. |
| An Authio bundle | A bundle downloaded from an earlier translation and corrected, or written by hand. | Taken from each user’s passwordHash. |
From Keycloak, Authio reads the realm, its roles, client scopes, clients, identity providers and users. It does not import client or identity provider secrets, which every Keycloak export masks, nor Keycloak’s own internal clients such as account and security-admin-console, nor its default scopes. Public clients arrive requiring PKCE.
Migrate from a provider
Export from your current provider
Produce the file the table above describes for your source. Treat it as a credential: it can hold password hashes and other secrets.
Read the export
Open Migration, pick your provider under Where are you coming from?, then choose the file or paste its contents. Read the export translates it into an Authio bundle without writing anything, and shows how many rows it understood in each section.
If something needs correcting, choose Download as a bundle, edit the JSON and upload it again with An Authio bundle as the source.
Rehearse the import
Rehearse the import runs the whole import without writing anything. The report counts what would be created, what is already there and what could not be used, section by section and row by row, with the line of the file and the reason for each problem.
Apply the import
Apply the import runs the same checks for real and reports line by line what was created. It only opens after a rehearsal of exactly the content on screen, and stays closed while the rehearsal refused part of the file, while your role cannot create some kind of resource the file contains, or when there is nothing to create.
Sections are applied in dependency order — realms, roles, scopes, clients, identity providers, consumers, secrets, signing keys, webhooks, users — whatever their order in the file. Each section needs the write permission of its own resource. If a real run stops partway, rehearse again before applying: part of the file may already have been written.
Import a file of users
In Users, choose Import from file, then choose a file or paste its contents. With a realm in scope, and optionally a client, every row is created there whatever the file says. With every realm in scope, each row names its own realm and, optionally, client.
CSV columns are matched by name, not by position, so the order they come out of your old system in does not matter:
| Column | Required | What it holds |
|---|---|---|
email | Yes | The person’s address. An address that already has an account is skipped. |
username | No | The username, when it differs from the address. |
firstname, lastname | No | The person’s name. |
phone | No | A phone number. |
password | No | A password in the clear, validated and hashed like one set in the console. |
passwordhash | No | The hash from your old system, in one of the formats below. A row cannot carry both a password and a hash. |
emailconfirmed | No | true, 1 or yes marks the address as already confirmed. |
roles | No | Role names separated by semicolons. The roles must already exist in your account. |
sendemail | No | Whether this person is e-mailed about the account. An empty cell, true, 1 or yes e-mails them; any other value leaves them out. With E-mail the people created turned off, nobody is e-mailed whatever it says. |
realm, client | Only with every realm in scope | Where the person is created. |
email,username,firstname,lastname,emailconfirmed,roles,passwordhash
ana@acme.com,ana,Ana,Souza,true,support;billing,$2a$12$R9h/cIPz0gi.URNNX3kh2OPST9/PgBkqquzi.Ss7KIUgO2t0jWMUW
bruno@acme.com,bruno,Bruno,Lima,false,support,Choose Rehearse, read the report, then Apply. When your plan includes the AI advisor, the rehearsal also says what the AI makes of the file, for example many rows that look alike in a way that suggests a mistake; see Authio AI.
From your own tooling, POST /api/v1/admin/realms/{realm}/clients/{client}/users/import takes the file in content, isJson to say it is JSON, dryRun, which is true unless you send false, and sendEmail, also true by default, where false e-mails nobody the file creates. See the Admin API.
Password hash formats
Only formats that describe themselves — the algorithm, its parameters and the salt are in the string — can be verified:
| Format | Shape |
|---|---|
| PBKDF2, Django form | pbkdf2_sha256$<iterations>$<salt>$<hash>, or pbkdf2_sha512 |
| PBKDF2, PHC form | $pbkdf2-sha256$i=<iterations>$<salt>$<hash>, or pbkdf2-sha512 |
| Salted SHA-2 (RFC 2307) | {SSHA256} or {SSHA512} followed by the Base64 of the hash and salt |
| bcrypt | $2a$, $2b$, $2x$ or $2y$ |
| Argon2id, PHC form | $argon2id$v=19$m=<memory>,t=<passes>,p=<lanes>$<salt>$<hash> |
Other formats are not refused at import
passwordhash empty for them and plan for them to set a password through password recovery on the hosted login.Before you go live
- Imported people get an e-mail, unless you turn it off. Each one is created like an account made in the console: a confirmation link when the address is not confirmed, otherwise a message that the account is ready. To create them without writing to anyone, turn off E-mail the people created in the import or the migration; the confirmation e-mail can be resent later from each person’s page. Otherwise, set
emailconfirmedfor addresses your old system had verified, and let people know the move is coming. - Webhooks fire. Every person created raises
user_createdon the client’s webhooks. Make sure your receivers expect a burst, or register endpoints after the import. - Quotas apply. Users count against your plan as they are created. When the quota is reached the users section stops and the account owner is notified once.
- Large files. One upload takes up to 50,000 rows; split bigger exports.
- Existing accounts are left alone. A row whose address already has an account is skipped, not updated.
- Secrets. Client and identity provider secrets do not come across from Keycloak; set them up in Authio before your applications switch over.
- Sessions do not move. Tokens and sessions from the old provider are not valid in Authio. Once an application points at its new issuer, such as
https://authio.api.hyperio.tech/realms/acme, people sign in again. - Delete the files. Exports and import files can hold hashes and secrets in the clear. Remove them once the migration is done.