UniAuth.ID

Migrating from Keycloak

Keycloak is the closest spec-for-spec peer to UniAuth — both are full-stack OAuth 2.0 / OIDC providers with SAML, SCIM, and back-channel logout. Migration is mostly realm → tenant mapping and swapping the issuer URL.

Endpoint Mapping

Keycloak prefixes endpoints with /realms/{realm}/protocol/openid-connect/…. UniAuth has a single issuer at https://uniauth.id. Tenanting is achieved via Organizations (/api/admin/organizations) and per-client org_id scoping.

KeycloakUniAuth
/realms/{realm}/.well-known/openid-configuration/.well-known/openid-configuration
/realms/{realm}/protocol/openid-connect/certs/.well-known/jwks.json
/realms/{realm}/protocol/openid-connect/auth/api/oauth/authorize
/realms/{realm}/protocol/openid-connect/token/api/oauth/token
/realms/{realm}/protocol/openid-connect/userinfo/api/oauth/userinfo
/realms/{realm}/protocol/openid-connect/logout/api/oauth/end-session
/realms/{realm}/protocol/openid-connect/revoke/api/oauth/revoke
/realms/{realm}/protocol/openid-connect/token/introspect/api/oauth/introspect
/realms/{realm}/protocol/openid-connect/auth/device/api/oauth/device
/realms/{realm}/clients-registrations/openid-connect/api/oauth/register
/realms/{realm}/protocol/saml/api/saml/sso
/realms/{realm}/protocol/saml/descriptor/api/saml/metadata
/realms/{realm}/scim/v2/api/scim/v2

Concept Mapping

Keycloak conceptUniAuth equivalentNotes
RealmOrganization + per-client org_id scopingSingle issuer for all tenants; apps are tenant-scoped
ClientOAuth Client (/account/developer or /admin/oauth-clients)Same OAuth 2.0 semantics; flag per-app via org_id
Client ScopeScope + Custom Claim Mapping per clientKeycloak's per-client-scope mapping ≈ custom_claims JSON on our oauth_clients
Realm RoleGroup (scim_groups)Emitted in 'groups' OIDC claim
Client RoleCustom Claim (role: "role")Per-client role claim via lib/custom-claims
Composite RoleNot native; resolve via nested group membership in your app
User Federation (LDAP)Upstream Identity Providers tableCurrently OIDC/OAuth2 upstreams; LDAP on roadmap
Identity Provider (social)connected_services via upstream_identity_providersDynamic list via /api/auth/providers
Authentication Flowauth_flows table + /api/admin/auth-flowsStep-based, customizable per client
Protocol MapperCustom Claim Mapping on oauth_clientsSame idea; static / user.field / role
Authz Services (UMA)Not implemented; use scope + claims + app-side policy
Event Listener SPIWebhooks + event streamingdispatchWebhook + /api/admin/event-streaming
Offline SessionsRefresh tokens (with rotation)30-day TTL, family-based replay detection
Session Clientoauth_refresh_tokens + sessionsPer-session tokens; full revocation on session delete

Claim Mapping

Keycloak and UniAuth both implement OIDC Core 1.0 claims, so standard claims (sub,email,name,preferred_username,locale,zoneinfo) match exactly.

Pairwise subject identifiers: UniAuth emits per-client pairwisesub values by default. Keycloak does notsub is the realm UUID. If your app correlates users across clients viasub, useemail instead.

User Migration

Option 1: Gradual via SCIM (recommended)

Keep Keycloak live during migration. Point Keycloak at UniAuth's SCIM 2.0 endpoint as a target (https://uniauth.id/api/scim/v2) so new users provision in both. Switch apps one by one to UniAuth's issuer.

Option 2: Export + bulk import

Use Keycloak's realm export:

# export from Keycloak
kc.sh export --dir /tmp/export --users different_files \
             --realm your-realm

Transform the resulting JSON to UniAuth's import schema (one object per user with email,firstName,lastName,groups) and POST to/api/admin/users/import (max 10,000 users per request, dry-run preview supported).

Passwords: Keycloak and UniAuth both hash passwords (Keycloak: PBKDF2/Argon2; UniAuth: Argon2id). Hashes don't migrate across providers. Users will set a new password on first UniAuth login, or use a magic link.

Option 3: Federation bridge

Configure UniAuth as a client of Keycloak temporarily: Keycloak authenticates users, UniAuth receives them via OIDC and provisions them on first login. Flip the relationship once adoption hits 80%.

Minimal Config Diff

- issuer:        https://kc.example.com/realms/your-realm
+ issuer:        https://uniauth.id
- jwks_uri:      https://kc.example.com/realms/your-realm/protocol/openid-connect/certs
+ jwks_uri:      https://uniauth.id/.well-known/jwks.json
- client_id:     your-kc-client
+ client_id:     uni_…
- client_secret: …
+ client_secret: unis_…

If your library uses OIDC discovery (set issuer only), there's usually no other change.

Next