KubeCon India 2026 (Mumbai) — Day 1 Deep Dives

09 · Keycloak Federated Client Authentication

Deep dive 9 of 17 · Security, policy & identity

Jun 18, 2026 · conferences · 20 min read · 4700 words intermediate

Keycloak federated client authentication — no more secrets.

conferences kubecon keycloak identity spiffe

Deep dive 9 of the KubeCon Mumbai 2026 series, closing the security cluster on identity. Rishabh Singh (Principal Technical Support Engineer, Red Hat) demonstrated Keycloak 26.6's federated client authentication: instead of storing and distributing OAuth client secrets to hundreds of microservices, Keycloak trusts the workload identity your platform already issues — a Kubernetes service-account token or a SPIFFE JWT-SVID. The client authenticates with an ephemeral, platform-signed JWT; Keycloak validates it against the cluster's JWKS. Zero stored secrets, automatic rotation, one identity model.

This is the natural endpoint of the security cluster. DD07 made container root safe; DD08 made SBOMs verifiable yet private; this one removes the last big pile of long-lived secrets — the OAuth client secret — by federating trust to the platform itself.

Keycloak in one slide

Keycloak implements the foundational standards — OAuth 2.0, SAML, OpenID Connect — and acts as a centralized authentication server: SSO, user management, authorization, so developers don't write custom security code. In the CNCF world it's the centralized identity apps delegate to, the API security plane issuing access tokens for microservices, and the manager of complex flows like MFA and external identity federation.

Static secrets fail at scale

Both classic OAuth flows lean on a stored client_secret. In the authorization code flow (user login), the web app exchanges an authorization code plus its client secret for tokens. In the client credentials flow (an app acting on its own behalf), the app posts client_credentials and its client secret to get an access token. Either way, that secret is a long-lived bearer credential — and at cloud-native scale, that's the problem:

Traditional client authWhy it breaks
Client secrets stored persistently in Keycloaka standing target
Distributed to every client instance (ConfigMaps)secret sprawl across the fleet
Manual rotation across all deploymentshigh coordination risk
Static credential leakagegrants persistent access
The cloud-native realization. Hundreds of microservices each need a dedicated OAuth client — and therefore a dedicated secret. But Kubernetes and SPIFFE already issue trusted, short-lived workload identity. So why have Keycloak manage a second, worse set of credentials? The fix: Keycloak validates platform-issued tokens instead of managing its own secrets. This is the same "trust the platform identity, don't mint your own" instinct behind SPIFFE — applied to OAuth client auth.

Federated client authentication — secretless clients

Keycloak 26.6 introduces federated client authentication: OIDC clients authenticate using ephemeral tokens, with native integration for Kubernetes and SPIFFE identities. Keycloak validates the platform identity, eliminating stored client secrets. The benefits map cleanly:

BenefitWhat it means
Zero secret managementcompletely eliminates storing client secrets.
Automatic rotationtoken lifecycle is handled by the platform.
Enhanced securitycredentials are short-lived and cryptographically signed.
Operational consistencyone identity model across all service meshes/clouds.

The outcome, in the speaker's words: a unified identity plane, not a segregated secrets store.

Where it sits among Keycloak's auth methods

AuthenticatorHow it worksUse case
Client ID & Secretsimple shared secretlegacy / non-containerized
Signed JWTclient signs its own JWTclients that generate/rotate their own keys
Signed JWT — Federatedexternal IdP JWT validationcloud-native (K8s SA, SPIFFE)
x509 Certificatemutual TLSolder enterprise environments

The new entry is "Signed JWT — Federated": starting in 26.6, Keycloak validates tokens issued by Kubernetes and SPIFFE rather than a key the client holds itself.

The shifting trust boundary

Trust extends from local secrets to external issuers Federated trust boundary K8s IdP SPIFFE Keycloak(local secrets)

Fig 1 — Keycloak's trust boundary grows to accept JWTs from authoritative external issuers.

  • Traditional trust: the boundary is defined by local client metadata and stored secrets.
  • The federation: the boundary extends to authoritative external issuers — Kubernetes and SPIFFE.
  • Keycloak's new role: it accepts external workload-identity tokens (JWTs) as proof of client identity.
  • Outcome: delegating identity validation is what enables federated client authentication.

The setup — realm, IdP, and two clients

The demo used a dedicated kubernetes realm to isolate workload identities, an Identity Provider configured for Kubernetes OIDC, and — neatly — two clients for comparison: web-k8s-client (federated-jwt, authenticates with a K8s SA token) and web-secret-client (traditional secret).

It's a preview feature, enabled with:

KC_FEATURES="client-auth-federated,kubernetes-service-accounts"

Establishing trust with Kubernetes

Three steps, one command:

# 1. Create the Kubernetes Identity Provider
kcadm.sh create identity-provider/instances -r kubernetes \
  -s alias=kubernetes \
  -s providerId=kubernetes \
  -s config.issuer=https://kubernetes.default.svc.cluster.local
# 2. Keycloak auto-fetches the JWKS from the issuer URL
# 3. Validates signature + claims (iss, sub, aud, exp), maps to a client
#    by issuer + subject matching

Client configuration — before and after

Before — web-secret-clientAfter — web-k8s-client
Client Authenticator: Client Id and SecretClient Authenticator: Signed JWT — Federated
Client Secret: •••••••••••• (stored)Identity provider: kubernetes
— rotate & distribute manuallyFederated subject: system:serviceaccount:default:my-serviceaccount

The application flow — a service-account JWT as client credential

On the Kubernetes side, the workload mounts a projected service-account token — short-lived, audience-scoped, auto-rotated:

serviceAccountName: demo-app
volumes:
  - name: keycloak-token
    projected:
      sources:
        - serviceAccountToken:
            path: token
            audience: https://keycloak/realms/k8s   # cryptographically scoped
            expirationSeconds: 3600                  # ephemeral
K8s Pod 1 inject SA token2 aud = realm URL 3 client_assertion= the JWT 4 Keycloakverify via K8s IdP

Fig 2 — inject → audience-bind → send as client_assertion → Keycloak verifies. No secret anywhere.

Keycloak then validates in three checks:

  • Signature check — verify the JWT signature against the Kubernetes JWKS endpoint.
  • Audience match — the token's aud claim must match the Keycloak realm issuer URL.
  • Client mapping — map the token's sub (service-account identity) to the provisioned Keycloak client.

If all pass, the client is authenticated and Keycloak issues the access token. The client sends the SA JWT in the standard client_assertion parameter — so it's ordinary OIDC client authentication, just with a platform-issued assertion instead of a stored secret.

Why the audience binding is the security crux. A projected SA token is scoped to a specific audience (the Keycloak realm URL). That means a token minted for Keycloak can't be replayed against another service, and a token for another audience can't be used here. Combined with the 3600-second expiry and auto-rotation, the credential is both narrowly scoped and short-lived — the opposite of a long-lived shared secret.

The core value

The summary was crisp. Problem: static client secrets require manual distribution and rotation. Solution: federated client authentication eliminates static secrets by trusting platform-issued workload identity tokens — a Kubernetes SA token or a SPIFFE JWT-SVID. The credential you were storing and rotating by hand is replaced by one the platform already issues, scopes, signs, and rotates for you.

FAQ

How is this different from a client signing its own JWT?

"Signed JWT" means the client holds its own key and signs assertions — you've still got a key to manage and rotate. "Signed JWT — Federated" delegates to an external issuer (Kubernetes/SPIFFE): the client presents a token the platform issued and signed, so there's no client-held key or secret at all.

What stops a stolen SA token from being replayed?

Audience scoping and short lifetime. The projected token's aud is bound to the Keycloak realm URL, so it's useless against any other service, and it expires (e.g. 3600s) and auto-rotates. Keycloak also checks signature, issuer, and subject before accepting it.

Does this work with SPIFFE too, or just Kubernetes?

Both. The feature federates to authoritative external issuers; the demo used Kubernetes OIDC, but SPIFFE JWT-SVIDs are a first-class target. The model is identical — validate a platform-issued workload JWT instead of a stored secret.

Is it production-ready?

In 26.6 it's a preview feature (enabled via KC_FEATURES). The pattern is sound and aligns with where the ecosystem is heading (workload identity over static secrets), but treat preview features with the usual caution and watch the Keycloak release notes for graduation.

Takeaways

  • OAuth client secrets are the last big pile of long-lived credentials — stored, distributed, and rotated by hand at microservice scale.
  • Keycloak 26.6 federates client auth to platform issuers, so clients authenticate with ephemeral, platform-signed JWTs.
  • "Signed JWT — Federated" validates K8s service-account tokens and SPIFFE JWT-SVIDs against the issuer's JWKS.
  • Audience binding + short expiry + auto-rotation make the credential narrowly scoped and self-expiring.
  • The win is a unified identity plane — one model across meshes and clouds, not a segregated, leak-prone secrets store.

That closes the security cluster. Next in the series — Deep dive 10: Building & Orchestrating Production-Ready Agentic AI Systems, opening the AI/agents block.

References

← prev: commit-then-disclose next: agentic AI systems →
© cvam — written in plaintext, served warm