- Authors
- Name
- 1. Password Fatigue and the Emergence of SSO
- 2. SSO Implementation Architecture
- 3. Technical Standard Comparison: SAML vs OAuth 2.0 vs OIDC
- 4. Keycloak Core Architecture
- 5. Production Deployment
- 6. Authentication Flows
- 7. Authorization System
- 8. Identity Brokering and Social Login
- 9. Application Integration in Practice
- 10. Token Relay Pattern in MSA Environments
- 11. Admin REST API Usage
- 12. High Availability (HA) Architecture
- 13. Monitoring and Observability
- 14. Conclusion
- References
1. Password Fatigue and the Emergence of SSO
As the number of applications used in modern business environments grows exponentially, Password Fatigue has become a serious security threat and a cause of productivity decline. SSO (Single Sign-On) technology emerged to solve this problem, allowing access to all services within a trust relationship with just a single authentication.
The core values gained through SSO are as follows:
- Centralized MFA: Manage multi-factor authentication in one place for consistent security policy enforcement
- Immediate Access Revocation: Block access at once from the IdP when employees leave or roles change
- Zero Trust Foundation: The basis for the "Never Trust, Always Verify" principle
2. SSO Implementation Architecture
SSO systems are divided into three models depending on the environment.
| Model | Method | Suitable Environment |
|---|---|---|
| Agent-based | Install agents on individual servers | Legacy environments |
| Agentless | Control traffic via API Gateway / Reverse Proxy | Cloud native |
| Identity Federation | Share identity across domains | B2B collaboration, multi-org |
3. Technical Standard Comparison: SAML vs OAuth 2.0 vs OIDC
| Category | SAML 2.0 | OAuth 2.0 | OIDC |
|---|---|---|---|
| Purpose | Authentication | Authorization | Authentication + Authorization |
| Data Format | XML (Assertion) | JSON | JSON (JWT) |
| Token | SAML Assertion | Access Token | ID Token + Access Token |
| Primary Use | Enterprise web apps | API authorization delegation | Mobile, SPA, MSA |
| Complexity | High | Medium | Low |
OIDC (OpenID Connect) adds an identity layer on top of OAuth 2.0 to handle authentication lightly in JWT format, and is the de facto standard for modern microservice environments.
4. Keycloak Core Architecture
Keycloak is an open-source IAM solution that supports SAML, OAuth 2.0, and OIDC standards. It is backed by Red Hat and is a CNCF incubating project.
4.1 Core Hierarchy
Keycloak Instance
└── Realm (logical isolation unit, tenant)
├── Clients (applications requesting authentication)
├── Users
├── Groups
├── Roles (Realm Roles + Client Roles)
├── Identity Providers (external IdP integration)
├── Authentication Flows (authentication flow definitions)
└── Authorization Services (fine-grained authorization policies)
- Realm: An isolated tenant space. The
masterRealm is for administration; create separate Realms for actual services - Client: OAuth/OIDC client applications (Confidential / Public / Bearer-only)
- Roles and Groups: The core of permission management. Realm Roles have global scope, Client Roles have specific client scope
4.2 Technology Stack
| Component | Role |
|---|---|
| Quarkus | High-performance Java framework runtime |
| Infinispan | In-memory distributed cache (session, token sync) |
| PostgreSQL (etc.) | Persistent data storage for users, policies |
5. Production Deployment
5.1 Docker Deployment
Development Mode (quick start, embedded H2 DB):
docker run --name keycloak -p 8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=change_me \
quay.io/keycloak/keycloak:latest \
start-dev
Production Mode (TLS + PostgreSQL required):
docker run --name keycloak -p 8443:8443 -p 9000:9000 -m 2g \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=change_me \
quay.io/keycloak/keycloak:latest start \
--hostname=https://auth.example.com \
--https-certificate-file=/opt/keycloak/conf/cert.pem \
--https-certificate-key-file=/opt/keycloak/conf/key.pem \
--db=postgres \
--db-url=jdbc:postgresql://db-host:5432/keycloak \
--db-username=keycloak \
--db-password=change_me \
--health-enabled=true \
--metrics-enabled=true
5.2 Kubernetes Operator Deployment
# Install CRDs and Operator
kubectl apply -f https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/26.5.4/kubernetes/keycloaks.k8s.keycloak.org-v1.yml
kubectl apply -f https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/26.5.4/kubernetes/keycloakrealmimports.k8s.keycloak.org-v1.yml
kubectl create namespace keycloak
kubectl -n keycloak apply -f https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/26.5.4/kubernetes/kubernetes.yml
Keycloak CR Manifest:
apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
name: production-kc
namespace: keycloak
spec:
instances: 3
db:
vendor: postgres
host: postgres-db
usernameSecret:
name: keycloak-db-secret
key: username
passwordSecret:
name: keycloak-db-secret
key: password
http:
tlsSecret: keycloak-tls-secret
hostname:
hostname: auth.example.com
proxy:
headers: xforwarded
5.3 keycloak.conf Production Configuration
# Hostname
hostname=https://auth.example.com
hostname-admin=https://admin.auth.example.com
# Database
db=postgres
db-url=jdbc:postgresql://db:5432/keycloak
db-username=keycloak
db-password=${vault.db-password}
db-pool-max-size=100
# TLS
http-enabled=false
https-port=8443
https-certificate-file=/etc/certs/tls.crt
https-certificate-key-file=/etc/certs/tls.key
# Proxy
proxy-headers=xforwarded
# Cache / Clustering
cache=ispn
# Health & Metrics (port 9000)
health-enabled=true
metrics-enabled=true
# Logging
log=console,file
log-console-output=json
6. Authentication Flows
6.1 OIDC Endpoints
All endpoints can be auto-discovered from the Well-Known URL.
GET /realms/{realm}/.well-known/openid-configuration
| Endpoint | Path |
|---|---|
| Authorization | /realms/{realm}/protocol/openid-connect/auth |
| Token | /realms/{realm}/protocol/openid-connect/token |
| Userinfo | /realms/{realm}/protocol/openid-connect/userinfo |
| Logout | /realms/{realm}/protocol/openid-connect/logout |
| JWKS | /realms/{realm}/protocol/openid-connect/certs |
| Introspect | /realms/{realm}/protocol/openid-connect/token/introspect |
6.2 Authorization Code Flow + PKCE
This is the recommended standard flow for web applications and SPA/mobile.
Step 1 - Authorization Request (Browser Redirect):
GET /realms/myrealm/protocol/openid-connect/auth?
response_type=code&
client_id=my-app&
redirect_uri=https://myapp.example.com/callback&
scope=openid profile email&
state=random-state-value&
code_challenge=BASE64URL(SHA256(code_verifier))&
code_challenge_method=S256
Step 2 - Token Exchange (Server-side):
curl -X POST \
https://auth.example.com/realms/myrealm/protocol/openid-connect/token \
-d "grant_type=authorization_code" \
-d "code=AUTHORIZATION_CODE" \
-d "redirect_uri=https://myapp.example.com/callback" \
-d "client_id=my-app" \
-d "client_secret=my-secret" \
-d "code_verifier=ORIGINAL_CODE_VERIFIER"
PKCE prevents code interception attacks. The code_verifier is a random string generated by the client, and the code_challenge is its SHA256 hash. The original code_verifier is submitted during token exchange for verification.
6.3 Client Credentials Flow
Used for service-to-service (M2M) authentication.
curl -X POST \
https://auth.example.com/realms/myrealm/protocol/openid-connect/token \
-d "grant_type=client_credentials" \
-d "client_id=my-service" \
-d "client_secret=my-service-secret" \
-d "scope=openid"
Confidential clients have a built-in Service Account. Assign roles at Clients, then my-service, then Service Account Roles.
6.4 Client Type Selection Guide
| Type | Secret | Use Case |
|---|---|---|
| Confidential | Yes | Server-side web apps, backend services |
| Public | No | SPA, mobile apps (PKCE required) |
| Bearer-only | Verify only | REST API services (Resource Server) |
7. Authorization System
7.1 RBAC (Role-Based Access Control)
Role-based access control assigns roles to users and permits or denies access based on those roles.
- Realm Role: Entire Realm scope (e.g.,
admin,user) - Client Role: Specific client scope (e.g.,
my-app:editor) - Composite Role: A higher-level role that bundles multiple roles
7.2 ABAC (Attribute-Based Access Control)
Attribute-based access control dynamically makes decisions by combining user IP, access time, department information, etc.
Keycloak Authorization Services supports various policy types.
| Policy Type | Description |
|---|---|
| Role-based | Evaluate Realm/Client roles |
| Group-based | Evaluate group membership |
| Time-based | Time period restrictions |
| Client-based | Requesting client restrictions |
| JavaScript | Custom JS conditions |
| Regex | Pattern matching |
| Aggregated | Combine multiple policies |
7.3 Permission Model
"Can X (user) perform Y (action) on Z (resource)?"
# Check permissions with RPT (Requesting Party Token) request
curl -X POST \
https://auth.example.com/realms/myrealm/protocol/openid-connect/token \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
-d "audience=my-resource-server"
Decision strategies:
- Affirmative: Access is granted if at least one policy permits
- Unanimous: Access is granted only if all policies permit
8. Identity Brokering and Social Login
Keycloak natively supports integration with external IdPs.
8.1 Supported IdPs
| Type | Supported List |
|---|---|
| Social Login | Google, GitHub, Facebook, Microsoft, LinkedIn, GitLab, etc. |
| Protocol-based | OIDC v1.0, OAuth 2.0, SAML 2.0 |
8.2 Google IdP Configuration Example
- Create OAuth 2.0 credentials in Google Cloud Console
- Redirect URI:
https://auth.example.com/realms/{realm}/broker/google/endpoint - Keycloak Admin Console then Identity Providers then add Google
- Enter Client ID and Client Secret
8.3 LDAP/Active Directory Integration
Integration with LDAP is available through User Federation.
| Setting | Active Directory | General LDAP |
|---|---|---|
| Connection URL | ldaps://ad-server:636 | ldap://ldap-server:389 |
| Users DN | OU=Users,DC=corp,DC=example,DC=com | ou=People,dc=example,dc=com |
| Username Attribute | sAMAccountName | uid |
| UUID Attribute | objectGUID | entryUUID |
Synchronization modes:
- READ_ONLY: Read-only from LDAP (safest)
- WRITABLE: Can modify LDAP attributes from Keycloak
- UNSYNCED: Store changes only locally in Keycloak
9. Application Integration in Practice
9.1 Spring Boot Resource Server
application.yml:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://auth.example.com/realms/myrealm
jwk-set-uri: https://auth.example.com/realms/myrealm/protocol/openid-connect/certs
SecurityConfig.java:
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(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(jwtAuthenticationConverter())
)
);
return http.build();
}
private JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
converter.setAuthoritiesClaimName("realm_access.roles");
converter.setAuthorityPrefix("ROLE_");
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(converter);
return jwtConverter;
}
}
9.2 Next.js (Auth.js / NextAuth.js)
.env.local:
AUTH_KEYCLOAK_ID=my-nextjs-app
AUTH_KEYCLOAK_SECRET=my-client-secret
AUTH_KEYCLOAK_ISSUER=https://auth.example.com/realms/myrealm
auth.ts (Auth.js v5):
import NextAuth from 'next-auth'
import Keycloak from 'next-auth/providers/keycloak'
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [Keycloak],
})
Register the Redirect URI in the Keycloak Client settings:
https://myapp.example.com/api/auth/callback/keycloak
9.3 React SPA (keycloak-js)
import Keycloak from 'keycloak-js'
const keycloak = new Keycloak({
url: 'https://auth.example.com',
realm: 'myrealm',
clientId: 'my-spa',
})
// Initialize with Authorization Code Flow + PKCE
const authenticated = await keycloak.init({
onLoad: 'login-required',
pkceMethod: 'S256',
checkLoginIframe: false,
})
// Use token for API calls
const response = await fetch('/api/data', {
headers: {
Authorization: `Bearer ${keycloak.token}`,
},
})
// Auto-refresh before token expiry
keycloak.onTokenExpired = () => {
keycloak.updateToken(30).catch(() => keycloak.login())
}
// Check roles
keycloak.hasRealmRole('admin')
keycloak.hasResourceRole('editor', 'my-resource-server')
9.4 Node.js Express
const express = require('express')
const session = require('express-session')
const Keycloak = require('keycloak-connect')
const app = express()
const memoryStore = new session.MemoryStore()
app.use(
session({
secret: 'my-secret',
resave: false,
saveUninitialized: true,
store: memoryStore,
})
)
const keycloak = new Keycloak(
{ store: memoryStore },
{
realm: 'myrealm',
'auth-server-url': 'https://auth.example.com/',
resource: 'my-node-app',
}
)
app.use(keycloak.middleware())
// Authentication required endpoint
app.get('/api/protected', keycloak.protect(), (req, res) => {
res.json({ message: 'Authenticated!' })
})
// Specific role required
app.get('/api/admin', keycloak.protect('realm:admin'), (req, res) => {
res.json({ message: 'Admin access granted' })
})
app.listen(3000)
10. Token Relay Pattern in MSA Environments
In a microservice architecture, the API Gateway integrates with Keycloak to obtain a JWT token, then relays requests to internal services.
[Client] → [API Gateway] → [Keycloak] (obtain/verify token)
↓
Authorization: Bearer <JWT>
↓
[Service A] → [Service B] → [Service C]
Each service does not need to communicate directly with the IdP; it only verifies the JWT signature locally, achieving excellent performance and security. Simply fetch and cache the public key from Keycloak's JWKS endpoint (/protocol/openid-connect/certs).
11. Admin REST API Usage
11.1 Token Acquisition
# Password method
ACCESS_TOKEN=$(curl -s -X POST \
"https://auth.example.com/realms/master/protocol/openid-connect/token" \
-d "grant_type=password" \
-d "client_id=admin-cli" \
-d "username=admin" \
-d "password=admin-password" | jq -r '.access_token')
# Service Account method (recommended for automation)
ACCESS_TOKEN=$(curl -s -X POST \
"https://auth.example.com/realms/master/protocol/openid-connect/token" \
-d "grant_type=client_credentials" \
-d "client_id=my-admin-client" \
-d "client_secret=my-admin-secret" | jq -r '.access_token')
11.2 User Management
# Create user
curl -X POST \
"https://auth.example.com/admin/realms/myrealm/users" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"username": "newuser",
"email": "newuser@example.com",
"enabled": true,
"credentials": [{
"type": "password",
"value": "temp-password",
"temporary": true
}]
}'
# Search users
curl -X GET \
"https://auth.example.com/admin/realms/myrealm/users?username=johndoe" \
-H "Authorization: Bearer $ACCESS_TOKEN"
# Assign roles
curl -X POST \
"https://auth.example.com/admin/realms/myrealm/users/{user-id}/role-mappings/realm" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '[{"id": "ROLE_ID", "name": "admin"}]'
12. High Availability (HA) Architecture
12.1 Distributed Architecture
┌─────────────┐
│ Load Balancer│
└──────┬──────┘
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│Keycloak 1│ │Keycloak 2│ │Keycloak 3│
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ Infinispan │ (session sync) │
└──────────────┴───────────────┘
│
┌──────┴──────┐
│ PostgreSQL │
│ (shared DB)│
└─────────────┘
- Static data (users, policies): stored in the shared DB (PostgreSQL)
- Dynamic data (sessions, tokens): synchronized across nodes via Infinispan distributed cache
12.2 Kubernetes Health Probe Configuration
spec:
containers:
- name: keycloak
livenessProbe:
httpGet:
path: /health/live
port: 9000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 9000
initialDelaySeconds: 30
periodSeconds: 10
startupProbe:
httpGet:
path: /health/started
port: 9000
failureThreshold: 30
periodSeconds: 5
12.3 Cross-DC Replication (External Infinispan)
For cases requiring synchronization across geographically separated data centers:
cache=ispn
cache-remote-host=infinispan.example.com
cache-remote-port=11222
cache-remote-username=keycloak
cache-remote-password=change_me
cache-remote-tls-enabled=true
13. Monitoring and Observability
13.1 Prometheus Metrics
# prometheus.yml
scrape_configs:
- job_name: 'keycloak'
metrics_path: '/metrics'
scheme: https
static_configs:
- targets: ['keycloak-host:9000']
Enabling event metrics:
bin/kc.sh start \
--metrics-enabled=true \
--event-metrics-user-enabled=true \
--event-metrics-user-tags=realm,client,idp \
--event-metrics-user-events=LOGIN,LOGIN_ERROR,LOGOUT,TOKEN_REFRESH
13.2 Grafana Dashboards
Keycloak provides official Grafana dashboards.
| Dashboard | Purpose |
|---|---|
keycloak-troubleshooting-dashboard.json | SLI and diagnostic analysis |
keycloak-capacity-planning-dashboard.json | Load estimation, login flow analysis |
Download: github.com/keycloak/keycloak-grafana-dashboard
14. Conclusion
Here is a summary of key points for successful SSO implementation.
- Protocol: Default to OIDC; apply SAML only for legacy enterprise apps
- Authorization: Start with RBAC as the base structure; combine with ABAC when fine-grained control is needed
- High Availability: At least 3 nodes + Infinispan distributed cache + shared DB configuration
- Application Integration: Spring Boot uses Resource Server JWT verification, SPA uses keycloak-js + PKCE, Next.js uses Auth.js
- Monitoring: Track login failure rates, token issuance volumes, etc., with Prometheus metrics + Grafana dashboards
Keycloak is the cornerstone of next-generation digital identity governance, providing all of this as open source.
References
- Keycloak Official Documentation. https://www.keycloak.org/guides
- Keycloak Production Configuration. https://www.keycloak.org/server/configuration-production
- Keycloak Operator Deployment. https://www.keycloak.org/operator/basic-deployment
- Keycloak OIDC Integration. https://www.keycloak.org/securing-apps/oidc-layers
- Keycloak JavaScript Adapter. https://www.keycloak.org/securing-apps/javascript-adapter
- Keycloak Node.js Adapter. https://www.keycloak.org/securing-apps/nodejs-adapter
- Keycloak Authorization Services. https://www.keycloak.org/docs/latest/authorization_services/
- Keycloak Admin REST API. https://www.keycloak.org/docs-api/latest/rest-api/index.html
- Keycloak Distributed Caching. https://www.keycloak.org/server/caching
- Keycloak High Availability. https://www.keycloak.org/high-availability/introduction
- Keycloak Metrics & Health. https://www.keycloak.org/observability/configuration-metrics
- Keycloak Grafana Dashboards. https://github.com/keycloak/keycloak-grafana-dashboard
- Auth.js Keycloak Provider. https://authjs.dev/getting-started/providers/keycloak
- OAuth 2.0 RFC 6749. https://datatracker.ietf.org/doc/html/rfc6749
- OpenID Connect Core 1.0. https://openid.net/specs/openid-connect-core-1_0.html