Skip to content

Keycloak Configuration

Keycloak is the identity and access management component of the platform. All of its configuration is code: three Jinja2 templates under infra/deployment/templates/configs/ are rendered per deployment stage into infra/configs/keycloak/ and mounted into the Keycloak container.

FileContainsApplied
keycloak-realm.json.j2The aihub realm: clients, scopes, roles, flows, …First container start only (--import-realm)
keycloak-identity-providers.json.j2External IdPs (Azure AD) and their mappersEvery container start (partialImport, overwrite)
keycloak-entrypoint.sh.j2Env-var substitution, import orchestration, kcadm reconciliationEvery container start

The aihub realm

The realm defines the platform's login behavior: users sign in with their e-mail address (registrationEmailAsUsername), self-registration is disabled (users come from the identity provider or are created by an admin), brute-force protection and "remember me" are enabled, and the custom aihub login theme is applied. TLS is required for external requests on all stages except dev. The realm binds a custom browser flow (browser-aihub, see Langfuse Sysadmin Gate).

Realm roles

Only two realm roles exist — everything fine-grained is handled by tenant-scoped roles inside the platform, not in Keycloak:

RoleEffect
AIHubAccessRequired to log in at all. Users without it are denied at the IdP post-login check.
AIHubSysAdminPlatform administrator: gates the oauth2-proxy admin tools (Dagster, Attu, SeaweedFS, Backup) and Langfuse, and marks superusers in the API.

Tenant groups

Tenant membership is modeled as Keycloak groups under /tenants/<tenant-id>. The startup tenant group is created by the realm import and set as a default group, so every new user lands in it. The tenants client scope exposes the group paths as a token claim, which the API uses to resolve a user's tenants (see ADR 2026_02_20_keycloak_tenant_assignment_via_groups).

Clients

ClientTypeUsed by
aihub-frontendPublic, PKCEMain web UI and sysadmin web UI (same realm cookie → shared SSO)
openwebuiConfidentialOpen WebUI chat interface (OIDC login, receives the roles claim)
oauth2-proxy-dagster / -datalake / -attu / -backupConfidentialThe oauth2-proxy sidecars in front of the admin UIs; access restricted via the roles claim (AIHubSysAdmin)
langfuseConfidential, PKCELangfuse native Keycloak SSO; carries the langfuse-sysadmin-gate marker scope
aihub-api-serviceService account onlyThe API backend; holds realm-management roles (manage-users, view-groups, …) for user/group administration

Client scopes

Besides the standard openid / profile / email scopes, the realm defines:

ScopeMapperPurpose
rolesRealm-role mapper → roles claimLets clients (frontend, oauth2-proxies, …) see realm roles
tenantsGroup-membership mapper → tenants claimTenant resolution from /tenants/... group paths
langfuse-sysadmin-gatenone (marker scope)Activates the Langfuse deny gate in the authentication flows

Identity brokering (Azure AD)

The realm acts as an identity broker (see ADR 2026_02_28_keycloak_as_identity_broker): the azure-ad provider logs users in against Microsoft Entra ID (PKCE, syncMode: FORCE). Its mappers copy e-mail, first/last name and username from the Azure token, and two role mappers translate the Azure app-role claims AIHubAccess and AIHubSysAdmin into the realm roles of the same name. Because the sync mode is FORCE, the roles are re-synced on every broker login — removing an app role in Azure removes the realm role at the user's next login (and vice versa); manually assigned realm roles do not survive for brokered users. The provider's post-login flow denies any user whose token lacks AIHubAccess.

Seeded users

The realm import creates one superuser (credentials and roles from the SUPERUSER_* environment variables — by default carrying AIHubAccess and AIHubSysAdmin) and the service-account user backing aihub-api-service.

Authentication flows

Two custom mechanisms extend the built-in flows:

  • Post Broker Login - AIHubAccess Check — runs after every Azure AD login and denies users without the AIHubAccess realm role.
  • browser-aihub + the Langfuse sysadmin gate — the realm's browser flow with a conditional deny that restricts Langfuse logins to AIHubSysAdmin. The mechanism, the reasons for replicating the built-in browser flow, and the structural pitfalls are documented in Langfuse Sysadmin Gate.

How configuration reaches running instances

The entrypoint script substitutes environment variables into the JSON templates (pure-bash envsubst; the Keycloak image ships no template tooling), then applies three layers with different lifecycles:

  1. Realm JSON — imported via --import-realm on the first start only. Keycloak never re-imports an existing realm, so realm-file changes do not reach already-initialized databases by themselves.
  2. Identity providers — applied on every start via the admin API (partialImport with overwrite), so IdP and mapper changes roll out with a container restart.
  3. Langfuse sysadmin gate — authentication flows are not supported by partialImport, so the entrypoint reconciles the gate idempotently via kcadm on every start (details in Langfuse Sysadmin Gate).

Built with ❤️ in Switzerland 🇨🇭