Security by Default: Keycloak as the Central Identity

Security by Default: Keycloak as the Central Identity

Keycloak in a real-world setup: realms, clients, and why we dropped shared secrets.

Authentication is a topic developers do not enjoy touching. Too specific, too risky, too boring. The result: every service has its own auth logic, often copy-pasted from tutorials, at varying levels of maturity. Anyone who has ever debugged auth bugs in production knows how expensive this approach is.

Why Keycloak

Keycloak is not a shiny novelty but a mature project (now 16 years old) well established in enterprise contexts. It speaks standards-compliant protocols: OIDC, SAML, token exchange. It runs on Kubernetes. It can be administered via UI, REST, or admin CLI.

There are alternatives: Authentik, Ory, or commercial offerings like Auth0. We deliberately chose the more mature — if heavier — Keycloak, simply because our customers in regulated industries often explicitly ask for Keycloak compatibility.

Realms and clients: the model we need

For every tenant we have a separate realm. That matters because Keycloak realms are real isolation boundaries — a compromise in one realm does not affect others. Within a realm live all clients (frontend SPAs, backend services, machine-to-machine accounts) and all users.

The user source is configured per realm. Some tenants have an LDAP sync, others an Azure AD provider, others a native user database. The application layer sees no difference — it gets an OIDC token and trusts it.

No shared secrets

A common anti-pattern: services authenticate to each other with a shared secret living in some environment variable. That does not scale. A leak means rotation for all services. A new service means access to secret management.

The clean alternative is service account tokens: every service has a Keycloak client with its own secret, fetches a token, and sends it on the API call. The receiving service verifies the token against Keycloak (or via a JWKS cache). The token is short-lived (5 minutes) and can be rotated without a big rollout.

PKCE for SPAs

For frontend applications, we replaced the classic implicit-flow and code-flow options with Authorization Code Flow + PKCE. The difference: no client secrets in the browser, no tokens in the URL fragment. Instead, the client generates a cryptographic proof of possession that Keycloak validates against the initial request.

Keycloak has supported PKCE natively for several years. The frontend library oidc-client-ts plays perfectly with it. The migration effort was 2 person-days per SPA — manageable.

Lessons learned

  • Cache tokens aggressively. JWKS endpoints on Keycloak are cheap, but at 10k+ requests/min it gets painful. In-memory caching with a 1-hour TTL is a good compromise.
  • Choose session timeouts deliberately. Our initial default — 30-minute access token, 8-hour refresh token — was too generous. For admin tools we reduced it to 15min / 4h.
  • Structure logs cleanly. Keycloak produces a lot of noise by default. A targeted logback filter on the events we care about (login failures, token rotations, admin actions) is what made our Grafana dashboards usable.

Outlook

The next topic is delegated access — user A grants user B limited rights (e.g. "broker sees policies of their customer"). Keycloak has the UMA protocol for that, but it has proven hard to get into. We are currently evaluating whether to bet on Keycloak Authorization Services or implement delegation in our own service layer.

SOVAIA (Ein Produkt der KIINNO AG)

Baarerstrasse 52
6300 Zug
Schweiz


plus 4 1 4 1 5 1 0 0 6 7 5