By the way, I wrote this script to convert my export:
import json
result = []
with open('keycloak-export.json', 'r') as file:
data = json.load(file)
for user in data['users']:
if 'service-account' in user['username']:
continue # Skip service accounts for now
pw_data = next((p for p in user['credentials'] if p['type'] == 'password'), None)
if pw_data is None:
raise ValueError(f"User {user['username']} does not have a password credential.")
pw_sec = json.loads(pw_data['secretData'])
pw_cred = json.loads(pw_data['credentialData'])
if pw_cred['algorithm'] != 'argon2':
raise ValueError(f"User {user['username']} does not have an argon2id password credential.")
if pw_cred['additionalParameters']['version'] != ['1.3']:
raise ValueError(f"User {user['username']} does not have an argon2id v1.3 password credential.")
if pw_cred['additionalParameters']['type'] != ['id']:
raise ValueError(f"User {user['username']} does not have an argon2id password credential of type 'id'.")
pw_m = pw_cred['additionalParameters']['memory'][0]
pw_t = pw_cred['hash_iterations'] # This should be in camelCase, but it's a bad word
pw_p = pw_cred['additionalParameters']['parallelism'][0]
pw_salt = str(pw_sec['salt']).rstrip('=')
pw_hash = str(pw_sec['value']).rstrip('=')
result.append({
"username": user['username'],
"email": user['email'],
"email_verified": user['emailVerified'],
"given_name": user['firstName'],
"nickname": user['firstName'],
"family_name": user['lastName'],
"name": f"{user['firstName']} {user['lastName']}",
"custom_password_hash": {
"algorithm": "argon2",
"hash": {
"value": f"$argon2id$v=19$m={pw_m},t={pw_t},p={pw_p}${pw_salt}${pw_hash}"
}
}
})
with open('auth0-import.json', 'w') as file:
json.dump(result, file, indent=4)
print(f"Exported {len(result)} users to auth0-import.json")