Skip to content

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

PathUse it whenWhere
Migrate from a providerYou 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 usersYou 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

SourceFile to uploadPasswords
KeycloakA 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.
Auth0The 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 CognitoThe user pool exported to CSV.Cognito exports no password material, so everyone sets a new password. Attributes, confirmed addresses and phone numbers come across.
OktaThe 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 bundleA 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

  1. 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.

  2. 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.

  3. 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.

  4. 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:

ColumnRequiredWhat it holds
emailYesThe person’s address. An address that already has an account is skipped.
usernameNoThe username, when it differs from the address.
firstname, lastnameNoThe person’s name.
phoneNoA phone number.
passwordNoA password in the clear, validated and hashed like one set in the console.
passwordhashNoThe hash from your old system, in one of the formats below. A row cannot carry both a password and a hash.
emailconfirmedNotrue, 1 or yes marks the address as already confirmed.
rolesNoRole names separated by semicolons. The roles must already exist in your account.
sendemailNoWhether 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, clientOnly with every realm in scopeWhere the person is created.
users.csvPlain text
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:

FormatShape
PBKDF2, Django formpbkdf2_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

A hash in any other format — scrypt, a bare digest without its salt, PBKDF2 over SHA-1 — is stored, but it will never match, and that person cannot sign in with their old password. Leave 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 emailconfirmed for addresses your old system had verified, and let people know the move is coming.
  • Webhooks fire. Every person created raises user_created on 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.

Next steps