Skip to content

Keycloak

Keycloak is an open-source identity and access management platform. Unlike Auth0 and Microsoft Entra ID, Keycloak is self-hosted: you run a Keycloak instance (in the same cluster or externally) and the Governance Platform authenticates against it.

The platform requires a dedicated Keycloak realm containing three OAuth clients:

  • Frontend client (governance-platform-frontend) - public SPA client used by Governance Studio for sign-in
  • Backend client (governance-platform-backend) - confidential client with a service account, used by the Auth Service for user management
  • Worker client (governance-worker) - confidential machine-to-machine client used by the governance worker

The keycloak-bootstrap Helm chart automates all of this: it runs a one-time Kubernetes Job that creates the realm, the three clients, the platform’s custom authorization scopes, and an initial platform-admin user via the Keycloak Admin REST API. Run it before deploying the platform chart - the services need valid client credentials at startup.

  • A running Keycloak instance, or a cluster where one can be deployed (this guide includes an in-cluster option)
  • Keycloak admin credentials for the master realm
  • kubectl and helm access to the deployment namespace
  • A checkout of the deployment repository for the keycloak-bootstrap chart

After completing the setup steps below, the platform configuration files will contain:

values.yaml:

values.yaml
auth-service:
config:
idp:
provider: "keycloak"
issuer: "https://<keycloak-host>/realms/governance" # Verified in step 6
keycloak:
realm: "governance"
adminUrl: "https://<keycloak-host>" # Keycloak base URL
clientId: "governance-platform-frontend"
enableUserManagement: true
tokenExchange:
enabled: true
keyId: "auth-service-001" # Identifier for the token exchange key
governance-service:
config:
keycloakUrl: "https://<keycloak-host>"
keycloakRealm: "governance"
governance-studio:
config:
keycloakUrl: "https://<keycloak-host>"
keycloakRealm: "governance"
keycloakClientId: "governance-platform-frontend"
# Post-install hook: seeds the governance database with the organization
# and the platform-admin user created by the bootstrap
keycloak:
createOrganization: true
realmName: "governance"
createPlatformAdmin: true
platformAdminEmail: "" # Defaults to admin@<global.domain>

secrets.yaml:

secrets.yaml
global:
secrets:
create: true
auth:
provider: "keycloak"
keycloak:
secretName: "platform-keycloak"
values:
serviceAccountClientId: "governance-platform-backend"
serviceAccountClientSecret: "<backend-client-secret>" # Retrieved in step 5
tokenExchangePrivateKey: | # Generate with: openssl genrsa 2048
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
governanceWorker:
secretName: "platform-governance-worker"
values:
encryptionKey: "GENERATE_WORKER_KEY" # Generate with: openssl rand -base64 32
clientId: "governance-worker"
clientSecret: "<worker-client-secret>" # Retrieved in step 5

Note: the frontend client ID is configured in values.yaml, not secrets.yaml - it is a public client with no secret.

Section titled “Quick Start (Bootstrap Chart - Recommended)”

If you already run Keycloak, note its URL and admin credentials and skip to step 2. Otherwise, deploy Keycloak into the cluster with the Bitnami chart:

Terminal window
# Keycloak server admin password (master realm)
kubectl create secret generic keycloak-admin \
--from-literal=password="$(openssl rand -base64 32)" \
--namespace governance
helm upgrade --install keycloak bitnami/keycloak \
--namespace governance \
--values keycloak-values.yaml \
--wait \
--timeout 10m

See the deployment repository’s Keycloak guides for complete keycloak-values.yaml examples per cloud provider, including ingress and TLS settings.

Verify Keycloak is healthy before continuing:

Terminal window
kubectl run curl-test --rm -it --image=curlimages/curl --restart=Never \
--namespace governance -- \
curl -s -o /dev/null -w "%{http_code}" http://keycloak:9000/keycloak/health/ready

The bootstrap job authenticates to Keycloak with the keycloak-admin secret (created in step 1, or create it now with your external Keycloak’s admin password) and sets the initial Governance Studio admin user’s password from platform-admin:

Terminal window
# Governance Studio admin user password (governance realm user, not the Keycloak server admin)
kubectl create secret generic platform-admin \
--from-literal=password="$(openssl rand -base64 32)" \
--namespace governance

From the deployment repository checkout, start from the example values file:

Terminal window
cp charts/keycloak-bootstrap/examples/values.yaml bootstrap-values.yaml

Edit bootstrap-values.yaml, replacing the domain placeholders with the platform domain:

bootstrap-values.yaml
clients:
frontend:
redirectUris:
- "https://<platform-domain>/*"
webOrigins:
- "https://<platform-domain>"
backend:
redirectUris:
- "https://<platform-domain>/authService/*"
webOrigins:
- "https://<platform-domain>"
users:
admin:
email: "admin@<platform-domain>"

If Keycloak is not reachable at the in-cluster default (http://keycloak:8080/keycloak), set its URL:

keycloak:
url: "https://<keycloak-host>" # External or cross-namespace URL
Terminal window
helm upgrade --install keycloak-bootstrap ./charts/keycloak-bootstrap \
--namespace governance \
--values bootstrap-values.yaml \
--wait \
--timeout 10m

The deployment repository also provides a helper script that validates prerequisites, runs the chart, and monitors the job: ./scripts/keycloak/bootstrap-keycloak.sh -f bootstrap-values.yaml -n governance.

Monitor the job and confirm it completes:

Terminal window
kubectl get jobs -l app.kubernetes.io/instance=keycloak-bootstrap \
--namespace governance -w
kubectl logs job/keycloak-bootstrap --namespace governance -f

The job is idempotent - re-running it skips resources that already exist.

5. Retrieve the Auto-Generated Client Secrets

Section titled “5. Retrieve the Auto-Generated Client Secrets”

Keycloak generates the backend and worker client secrets during bootstrap. Retrieve both via the Admin API:

Terminal window
# Port-forward the Keycloak service (skip if Keycloak has an external URL)
kubectl port-forward svc/keycloak 8080:8080 --namespace governance &
# Get an admin token
ADMIN_PASS=$(kubectl get secret keycloak-admin --namespace governance \
-o jsonpath='{.data.password}' | base64 -d)
TOKEN=$(curl -s -X POST "http://localhost:8080/keycloak/realms/master/protocol/openid-connect/token" \
-d "username=admin" \
-d "password=$ADMIN_PASS" \
-d "grant_type=password" \
-d "client_id=admin-cli" | jq -r '.access_token')
# Backend client secret
curl -s -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/keycloak/admin/realms/governance/clients?clientId=governance-platform-backend" \
| jq -r '.[0].secret'
# Worker client secret
curl -s -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/keycloak/admin/realms/governance/clients?clientId=governance-worker" \
| jq -r '.[0].secret'
kill %1

Alternatively, use the admin console: Clientsgovernance-platform-backendCredentials tab (and the same for governance-worker).

Fill in the Helm Configuration sections:

  • The two client secrets from step 5
  • A token exchange private key: openssl genrsa 2048
  • The issuer URL, which you can verify with:
Terminal window
curl -s https://<keycloak-host>/realms/governance/.well-known/openid-configuration | jq '.issuer'

Then proceed to the Helm Deployment guide. The platform chart’s post-install hook creates the organization and links the platform-admin user automatically.

Alternative: Manual Setup via Admin Console

Section titled “Alternative: Manual Setup via Admin Console”

The bootstrap chart is strongly recommended - it configures the realm, clients, protocol mappers, custom scopes, and service account roles consistently. If you must configure Keycloak manually, replicate what the bootstrap creates:

  1. Create a governance realm with brute force protection enabled
  2. Create the three clients listed in the Overview: frontend as a public client with the platform domain in redirect URIs and web origins; backend and worker as confidential clients with service accounts enabled
  3. Grant the backend client’s service account the query-users and view-users realm-management roles
  4. Create the platform’s custom authorization scopes and assign the frontend client’s default scopes (openid, profile, email, roles, sub)
  5. Create a platform-admin realm user

See the keycloak-bootstrap chart README for the complete list of scopes, mappers, and defaults to replicate.

Confirm the realm’s OIDC discovery endpoint responds and the issuer matches values.yaml:

Terminal window
curl -s https://<keycloak-host>/realms/governance/.well-known/openid-configuration | jq '.issuer'

After deploying the platform, sign in to Governance Studio as platform-admin using the password from the platform-admin secret:

Terminal window
kubectl get secret platform-admin --namespace governance \
-o jsonpath='{.data.password}' | base64 -d
  • Require TLS on all Keycloak endpoints (sslRequired: external is the bootstrap default; use all if internal traffic should also be encrypted)
  • Keep brute force protection enabled on the governance realm (bootstrap default)
  • Store the Keycloak admin password, client secrets, and token exchange private key only in Kubernetes secrets or an approved secret manager
  • Leave self-registration disabled unless your onboarding process requires it
  • Rotate the backend and worker client secrets periodically - regenerate in Keycloak, then update the platform-keycloak and platform-governance-worker secrets in the same change window
  • Keep the Keycloak version patched; it is part of your authentication attack surface