Keycloak in Production: IAM for Enterprise Applications
Keycloak is an open-source Identity and Access Management (IAM) solution maintained by Red Hat. It provides Single Sign-On (SSO), OAuth 2.0, OpenID Connect (OIDC), and SAML 2.0 out of the box — making it one of the most capable self-hosted IAM platforms available.
Core concepts
Before diving into configuration, it helps to understand the key building blocks:
| Concept | Description |
|---|---|
| Realm | An isolated namespace for users, clients, and policies |
| Client | An application that delegates authentication to Keycloak |
| User | A person or service account managed within a realm |
| Role | A named permission assigned to users or clients |
| Identity Provider | An external IdP (e.g. Active Directory, Google) federated into the realm |
Deploying on Kubernetes
Keycloak ships with an official Kubernetes Operator. A minimal production-ready deployment:
apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
name: keycloak
namespace: iam
spec:
instances: 3 # HA: 3 pods, Infinispan cache clustering
db:
vendor: postgres
host: postgres.iam.svc
database: keycloak
usernameSecret:
name: keycloak-db-secret
key: username
passwordSecret:
name: keycloak-db-secret
key: password
http:
tlsSecret: keycloak-tls
hostname:
hostname: sso.example.internal
features:
enabled:
- token-exchange
- admin-fine-grained-authz
Keycloak uses Infinispan for distributed session and token caching across instances. With three replicas, the cluster can tolerate the loss of one node without session loss.
Configuring an OIDC client
For a backend API that validates JWTs issued by Keycloak:
{
"clientId": "orders-api",
"protocol": "openid-connect",
"publicClient": false,
"bearerOnly": true,
"attributes": {
"access.token.lifespan": "300"
}
}
A frontend SPA uses the Authorization Code flow with PKCE:
{
"clientId": "orders-spa",
"protocol": "openid-connect",
"publicClient": true,
"redirectUris": ["https://app.example.internal/*"],
"webOrigins": ["https://app.example.internal"],
"attributes": {
"pkce.code.challenge.method": "S256"
}
}
Token validation in a Java backend
Using the Keycloak Spring Boot adapter (or any JOSE library):
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
.jwkSetUri("https://sso.example.internal/realms/prod/protocol/openid-connect/certs")
)
);
return http.build();
}
}
The jwk-set-uri endpoint is served by Keycloak — the backend fetches the public keys
and validates the JWT signature locally, without a round-trip to Keycloak on every request.
User federation with Active Directory
Keycloak can sync users from an LDAP/Active Directory directory:
Keycloak realm → User Federation → LDAP provider
host: ldap.corp.example.internal:636 (LDAPS)
bindDn: CN=keycloak-svc,OU=ServiceAccounts,DC=corp,DC=example,DC=internal
usersDn: OU=Users,DC=corp,DC=example,DC=internal
searchScope: SUBTREE
syncPeriod: 3600 # full sync every hour
Group memberships from AD can be mapped to Keycloak roles via LDAP group mappers, so existing access control structures carry over without manual re-provisioning.
Fine-grained authorisation with UMA 2.0
For resource-level access control (beyond simple roles), Keycloak supports User-Managed Access (UMA 2.0). Resources, scopes, and policies are managed via the Keycloak Admin API and evaluated at runtime by the backend:
# Request a permission ticket
POST /realms/prod/protocol/openid-connect/token
grant_type=urn:ietf:params:oauth:grant-type:uma-ticket
&audience=orders-api
&permission=order#read
This allows per-resource, per-user decisions without hardcoding access rules in application code — a significant advantage in multi-tenant or document-oriented systems.