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.
| Keycloak | UniAuth |
|---|---|
| /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 concept | UniAuth equivalent | Notes |
|---|---|---|
| Realm | Organization + per-client org_id scoping | Single issuer for all tenants; apps are tenant-scoped |
| Client | OAuth Client (/account/developer or /admin/oauth-clients) | Same OAuth 2.0 semantics; flag per-app via org_id |
| Client Scope | Scope + Custom Claim Mapping per client | Keycloak's per-client-scope mapping ≈ custom_claims JSON on our oauth_clients |
| Realm Role | Group (scim_groups) | Emitted in 'groups' OIDC claim |
| Client Role | Custom Claim (role: "role") | Per-client role claim via lib/custom-claims |
| Composite Role | — | Not native; resolve via nested group membership in your app |
| User Federation (LDAP) | Upstream Identity Providers table | Currently OIDC/OAuth2 upstreams; LDAP on roadmap |
| Identity Provider (social) | connected_services via upstream_identity_providers | Dynamic list via /api/auth/providers |
| Authentication Flow | auth_flows table + /api/admin/auth-flows | Step-based, customizable per client |
| Protocol Mapper | Custom Claim Mapping on oauth_clients | Same idea; static / user.field / role |
| Authz Services (UMA) | — | Not implemented; use scope + claims + app-side policy |
| Event Listener SPI | Webhooks + event streaming | dispatchWebhook + /api/admin/event-streaming |
| Offline Sessions | Refresh tokens (with rotation) | 30-day TTL, family-based replay detection |
| Session Client | oauth_refresh_tokens + sessions | Per-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 not — sub 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-realmTransform 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.