Keycloak
Sign-in via Keycloak using OIDC. Backed by security-keycloak. Pairs with the Helm Keycloak addon for in-cluster deployments.
Configure a realm and client
In Keycloak:
- Create a realm (e.g.
dirigible). - Add a client (e.g.
dirigible-app). - Set Access Type to
confidential. Note the generated Client Secret. - Valid Redirect URIs:
https://dirigible.example.com/login/oauth2/code/keycloak. - Web Origins:
https://dirigible.example.com. - Add the platform's roles (
DEVELOPER,ADMINISTRATOR, plus any application roles you declared in*.rolesartefacts). - Assign every user who should be able to sign in the
offline_accessrealm role.
offline_access is not optional
The platform requests the offline_access scope on every login. A user without the matching realm role cannot sign in at all: Keycloak refuses the code-to-token exchange, and what the browser shows is the generic Invalid credentials, which names nothing. Keycloak's own event log holds the real reason (reason="Offline tokens not allowed for the user or client").
The platform requests nine scopes in total (openid, profile, roles, microprofile-jwt, email, phone, web-origins, address, offline_access) and Keycloak rejects the whole login if any one of them is not assigned to the client, so leave the client's default and optional scopes in place.
Enable the profile
SPRING_PROFILES_ACTIVE=keycloak
DIRIGIBLE_KEYCLOAK_AUTH_SERVER_URL=https://auth.example.com/realms/dirigible
DIRIGIBLE_KEYCLOAK_CLIENT_ID=dirigible-app
DIRIGIBLE_KEYCLOAK_CLIENT_SECRET=<secret>
DIRIGIBLE_HOST=https://dirigible.example.comDIRIGIBLE_KEYCLOAK_AUTH_SERVER_URL is the full realm URL, and it is both what the browser is sent to and what the issuer of the ID token is validated against, so it has to be the URL the browser actually uses. DIRIGIBLE_HOST is where the identity provider sends the user back: the redirect URI is <host>/login/oauth2/code/keycloak, which is the value the realm must allow.
Role mapping
The platform reads a user's roles from a groups claim in the token, not from realm_access.roles. On Keycloak the claim is conventionally groups, and a group named ADMINISTRATOR grants that platform role. Add a group membership mapper to the client (or a client scope it uses) that writes the user's groups into the claim, and make sure it emits them as a JSON array.
Multi-tenant deployments
One realm serves every tenant. Name the groups <tenantId>.<appId>.<role> and point DIRIGIBLE_TENANT_GROUPS_CLAIM at the claim that carries them; the signed-in user then picks which of their tenants to work in. See Tenant resolution.
Give the tenant groups a claim of their own
Do not put them in groups. The platform requests the microprofile-jwt scope, whose built-in mapper copies the user's realm roles into a claim named groups; read as tenant groups, those realm roles would become platform-wide roles, so a realm role called ADMINISTRATOR would grant platform administration to a user with no group at all. Dropping the scope is not an option, Keycloak then rejects the login. Write the groups to a claim of their own instead:
DIRIGIBLE_TENANT_GROUPS_CLAIM=dirigible_groupsTurn the group membership mapper's full group path off as well. With the path on, the claim reads /acme.library.Owner and the tenant id comes out as /acme.
First-party sign-in
Since 14.30 the keycloak profile supports the First-Party Sign-In building blocks: DIRIGIBLE_SECURITY_LOGIN_PAGE redirects unauthenticated browsers to an application page, and the kc_idp_hint passthrough deep-links federated users straight to their IdP. The native credential endpoint currently ships for Cognito; the Keycloak implementation can follow through the same internal contract.