Keycloak: Enterprise Identity and Access Management
Identity and Access Management (IAM) is the backbone of enterprise security. As organizations adopt microservices, APIs, and multi-cloud architectures, the need for centralized, standards-based authentication becomes critical. Keycloak has emerged as the leading open-source IAM solution, powering authentication for organizations ranging from startups to Fortune 500 companies.
This guide covers Keycloak architecture, OAuth 2.0/OIDC flows, integration patterns, and enterprise deployment considerations.
Why Keycloak?
Traditional authentication approaches—session cookies, custom token implementations, LDAP direct binds—don’t scale to modern distributed architectures. Keycloak addresses this with:
- Standards-based: OAuth 2.0, OpenID Connect, SAML 2.0
- Single Sign-On: One login across all applications
- Identity Brokering: Federate with external identity providers
- Fine-grained Authorization: Role-based and attribute-based access control
- Self-service: User registration, password recovery, account management
- Enterprise Ready: High availability, clustering, multi-tenancy
Core Concepts
Realms
A realm is a security domain managing a set of users, credentials, roles, and clients. Each realm is isolated—users in one realm cannot access another.
Master Realm (admin only)
├── Production Realm
│ ├── Users
│ ├── Clients (apps)
│ └── Identity Providers
├── Staging Realm
└── Development Realm
Best practice: Use separate realms for environments, not for tenants. Multi-tenancy is better handled at the application level.
Clients
A client is an application that requests authentication. Each client has:
- Client ID: Unique identifier
- Client Secret: For confidential clients
- Redirect URIs: Allowed callback URLs
- Protocol: OpenID Connect or SAML
Client types:
| Type | Description | Example |
|---|---|---|
| Confidential | Server-side apps that can keep secrets | Spring Boot backend |
| Public | Client-side apps that cannot keep secrets | React SPA, mobile app |
| Bearer-only | APIs that only validate tokens | REST microservice |
Users and Roles
Users can be:
- Created directly in Keycloak
- Imported from LDAP/Active Directory
- Federated from external identity providers
Roles provide authorization:
Realm Roles (global)
├── admin
├── user
└── auditor
Client Roles (app-specific)
├── order-service
│ ├── create-order
│ └── view-orders
└── inventory-service
├── manage-stock
└── view-inventory
Identity Brokering
Keycloak can delegate authentication to external identity providers:
- Social: Google, Facebook, GitHub, Apple
- Enterprise: SAML IdPs, OIDC providers
- Corporate: Active Directory, LDAP
User → Keycloak → External IdP → Keycloak → Application
↓
(Authentication)
OAuth 2.0 and OpenID Connect
Keycloak implements OAuth 2.0 for authorization and OpenID Connect (OIDC) for authentication.
Authorization Code Flow
The most secure flow for server-side applications:
1. User clicks "Login"
2. App redirects to Keycloak /auth endpoint
3. User authenticates (username/password, MFA, etc.)
4. Keycloak redirects back with authorization code
5. App exchanges code for tokens (server-side)
6. App receives access_token, refresh_token, id_token
Implementation (Spring Boot):
# application.yml
spring:
security:
oauth2:
client:
registration:
keycloak:
client-id: my-app
client-secret: ${KEYCLOAK_SECRET}
scope: openid,profile,email
provider:
keycloak:
issuer-uri: https://auth.example.com/realms/production
Authorization Code with PKCE
For public clients (SPAs, mobile apps), PKCE prevents authorization code interception:
// Generate PKCE challenge
const codeVerifier = generateRandomString(64);
const codeChallenge = await sha256(codeVerifier);
// Authorization request includes challenge
const authUrl = `${keycloakUrl}/auth?
response_type=code&
client_id=spa-app&
code_challenge=${codeChallenge}&
code_challenge_method=S256&
redirect_uri=${redirectUri}`;
// Token exchange includes verifier
const tokenResponse = await fetch(`${keycloakUrl}/token`, {
method: 'POST',
body: new URLSearchParams({
grant_type: 'authorization_code',
code: authorizationCode,
code_verifier: codeVerifier,
client_id: 'spa-app',
redirect_uri: redirectUri
})
});
Client Credentials Flow
For service-to-service authentication (no user involved):
curl -X POST "https://auth.example.com/realms/production/protocol/openid-connect/token" \
-d "grant_type=client_credentials" \
-d "client_id=backend-service" \
-d "client_secret=${CLIENT_SECRET}"
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 300,
"token_type": "Bearer"
}
Token Structure
Keycloak issues JWTs (JSON Web Tokens) containing claims:
{
"exp": 1699999999,
"iat": 1699999000,
"jti": "unique-token-id",
"iss": "https://auth.example.com/realms/production",
"sub": "user-uuid",
"typ": "Bearer",
"azp": "my-app",
"scope": "openid profile email",
"realm_access": {
"roles": ["user", "admin"]
},
"resource_access": {
"order-service": {
"roles": ["create-order", "view-orders"]
}
},
"name": "John Doe",
"email": "[email protected]"
}
Token Validation
APIs must validate tokens before processing requests:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("admin")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
.jwtAuthenticationConverter(jwtAuthConverter())
)
);
return http.build();
}
private JwtAuthenticationConverter jwtAuthConverter() {
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
converter.setAuthoritiesClaimName("realm_access.roles");
converter.setAuthorityPrefix("ROLE_");
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(converter);
return jwtConverter;
}
}
Integration Patterns
Pattern 1: API Gateway Integration
Centralize authentication at the gateway level:
The gateway validates tokens and forwards requests with user context (headers or propagated JWT).
Pattern 2: Service Mesh Integration
With Istio or similar, authentication happens at the sidecar:
# Istio RequestAuthentication
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
name: keycloak-auth
spec:
jwtRules:
- issuer: "https://auth.example.com/realms/production"
jwksUri: "https://auth.example.com/realms/production/protocol/openid-connect/certs"
Pattern 3: Direct Integration
Each service validates tokens independently:
Services cache the JWKS (public keys) and validate tokens locally without calling Keycloak for every request.
Single Sign-On Federation
SSO Across Applications
Once authenticated with Keycloak, users access all applications without re-authenticating. The session cookie stored by Keycloak enables seamless access to all connected applications.
Identity Provider Federation
Federate with enterprise identity providers. Keycloak acts as a broker, redirecting users to their corporate IdP for authentication and mapping the response back to the application.
Configuration for SAML IdP:
{
"alias": "corporate-saml",
"providerId": "saml",
"enabled": true,
"config": {
"entityId": "https://auth.example.com/realms/production",
"singleSignOnServiceUrl": "https://idp.corporate.com/sso",
"nameIDPolicyFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
"signatureAlgorithm": "RSA_SHA256"
}
}
User Federation
LDAP/Active Directory Integration
Synchronize users from corporate directories:
{
"name": "corporate-ldap",
"providerId": "ldap",
"config": {
"connectionUrl": "ldaps://ldap.corporate.com:636",
"usersDn": "ou=users,dc=corporate,dc=com",
"userObjectClasses": "inetOrgPerson, organizationalPerson",
"usernameAttribute": "uid",
"rdnAttribute": "uid",
"uuidAttribute": "entryUUID",
"bindDn": "cn=admin,dc=corporate,dc=com",
"bindCredential": "${LDAP_PASSWORD}"
}
}
Sync modes:
| Mode | Description |
|---|---|
| Import | Copy users to Keycloak database |
| Read-only | Query LDAP on demand |
| Writable | Sync changes back to LDAP |
High Availability Deployment
Clustered Architecture
┌──────────────────┐
│ Load Balancer │
└────────┬─────────┘
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│Keycloak 1│ │Keycloak 2│ │Keycloak 3│
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└─────────────┼─────────────┘
▼
┌────────────────────────┐
│ Infinispan Cluster │
│ (Session Cache) │
└────────────┬───────────┘
▼
┌────────────────────────┐
│ PostgreSQL (HA) │
│ (User/Config Data) │
└────────────────────────┘
Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
spec:
replicas: 3
template:
spec:
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:23.0
args: ["start"]
env:
- name: KC_DB
value: postgres
- name: KC_DB_URL
value: jdbc:postgresql://postgres:5432/keycloak
- name: KC_CACHE
value: ispn
- name: KC_CACHE_STACK
value: kubernetes
- name: KC_HOSTNAME
value: auth.example.com
ports:
- containerPort: 8080
Security Hardening
Recommended Settings
# Disable HTTP, enforce HTTPS
KC_HTTP_ENABLED=false
KC_HTTPS_CERTIFICATE_FILE=/certs/tls.crt
KC_HTTPS_CERTIFICATE_KEY_FILE=/certs/tls.key
# Strict hostname checking
KC_HOSTNAME_STRICT=true
KC_HOSTNAME=auth.example.com
# Session security
KC_SPI_STICKY_SESSION_ENCODER_INFINISPAN_SHOULD_ATTACH_ROUTE=false
Brute Force Protection
Enable in realm settings:
| Setting | Recommended Value |
|---|---|
| Max login failures | 5 |
| Wait increment | 60 seconds |
| Max wait | 15 minutes |
| Failure reset time | 12 hours |
Token Security
{
"accessTokenLifespan": 300,
"refreshTokenLifespan": 1800,
"ssoSessionIdleTimeout": 1800,
"ssoSessionMaxLifespan": 36000,
"accessTokenLifespanForImplicitFlow": 300
}
Monitoring and Operations
Key Metrics
| Metric | Description | Alert Threshold |
|---|---|---|
| Login success rate | % successful authentications | < 95% |
| Token validation latency | Time to validate JWT | > 50ms |
| Active sessions | Concurrent user sessions | Capacity based |
| Failed logins | Authentication failures | Anomaly based |
Health Endpoints
# Readiness (can accept traffic)
curl https://auth.example.com/health/ready
# Liveness (process is running)
curl https://auth.example.com/health/live
# Metrics (Prometheus format)
curl https://auth.example.com/metrics
Migration Considerations
From Legacy Systems
When migrating from custom authentication:
- Parallel run: Both systems active during transition
- User migration: Bulk import or lazy migration on first login
- Password handling: Hash migration or force password reset
- Session handling: Plan for session invalidation at cutover
Version Upgrades
Keycloak upgrade checklist:
□ Review release notes for breaking changes
□ Backup database
□ Test upgrade in staging environment
□ Update client adapters/libraries
□ Plan maintenance window
□ Execute upgrade with rollback plan ready
□ Validate authentication flows post-upgrade
Conclusion
Keycloak provides enterprise-grade IAM without the licensing costs of commercial alternatives. Its standards-based approach (OAuth 2.0, OIDC, SAML) ensures compatibility with virtually any application framework.
Key takeaways:
- Start with standards: Use OAuth 2.0/OIDC, not custom token schemes
- Design realms carefully: Environment separation, not tenant isolation
- Plan for federation: LDAP sync, SAML IdPs, social providers
- Deploy for HA: Clustered nodes, shared cache, database replication
- Monitor actively: Login success rates, token latency, session counts
For organizations requiring commercial support, Red Hat Single Sign-On (RH-SSO) provides the same capabilities with enterprise backing and extended maintenance cycles.
Keycloak: Enterprise Identity and Access Management
A guide to modern authentication and authorization.
Achraf SOLTANI — May 10, 2024
