The Sanctuary

Writing about interests; Computer Science, Philosophy, Mathematics and AI.

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

Keycloak Architecture

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:

TypeDescriptionExample
ConfidentialServer-side apps that can keep secretsSpring Boot backend
PublicClient-side apps that cannot keep secretsReact SPA, mobile app
Bearer-onlyAPIs that only validate tokensREST 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:

Authorization Code Flow

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:

API Gateway Pattern

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:

Service Mesh Pattern

# 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:

Direct Integration Pattern

Services cache the JWKS (public keys) and validate tokens locally without calling Keycloak for every request.

Single Sign-On Federation

SSO 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:

ModeDescription
ImportCopy users to Keycloak database
Read-onlyQuery LDAP on demand
WritableSync 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

# 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:

SettingRecommended Value
Max login failures5
Wait increment60 seconds
Max wait15 minutes
Failure reset time12 hours

Token Security

{
  "accessTokenLifespan": 300,
  "refreshTokenLifespan": 1800,
  "ssoSessionIdleTimeout": 1800,
  "ssoSessionMaxLifespan": 36000,
  "accessTokenLifespanForImplicitFlow": 300
}

Monitoring and Operations

Key Metrics

MetricDescriptionAlert Threshold
Login success rate% successful authentications< 95%
Token validation latencyTime to validate JWT> 50ms
Active sessionsConcurrent user sessionsCapacity based
Failed loginsAuthentication failuresAnomaly 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:

  1. Parallel run: Both systems active during transition
  2. User migration: Bulk import or lazy migration on first login
  3. Password handling: Hash migration or force password reset
  4. 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:

  1. Start with standards: Use OAuth 2.0/OIDC, not custom token schemes
  2. Design realms carefully: Environment separation, not tenant isolation
  3. Plan for federation: LDAP sync, SAML IdPs, social providers
  4. Deploy for HA: Clustered nodes, shared cache, database replication
  5. 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