Split View: 차세대 디지털 아이덴티티와 SSO, 그리고 Keycloak 실무 완벽 가이드
차세대 디지털 아이덴티티와 SSO, 그리고 Keycloak 실무 완벽 가이드
- 1. 패스워드 피로와 SSO의 등장
- 2. SSO 구현 아키텍처
- 3. 기술 표준 비교: SAML vs OAuth 2.0 vs OIDC
- 4. Keycloak 핵심 아키텍처
- 5. 프로덕션 배포
- 6. 인증(Authentication) 플로우
- 7. 인가(Authorization) 체계
- 8. Identity Brokering과 Social Login
- 9. 어플리케이션 연동 실무
- 10. MSA 환경의 Token Relay 패턴
- 11. Admin REST API 활용
- 12. 고가용성(HA) 아키텍처
- 13. 모니터링 및 관측성
- 14. 마무리
- References
1. 패스워드 피로와 SSO의 등장
현대 비즈니스 환경에서 사용하는 어플리케이션이 기하급수적으로 늘어나면서 **패스워드 피로도(Password Fatigue)**는 심각한 보안 위협이자 생산성 저하의 원인이 되었다. 이를 해결하기 위해 등장한 SSO(Single Sign-On) 기술은 단 한 번의 인증으로 신뢰 관계가 구축된 모든 서비스에 접근할 수 있게 해준다.
SSO를 통해 얻을 수 있는 핵심 가치는 다음과 같다.
- MFA 중앙화: 다요소 인증을 한 곳에서 관리하여 일관된 보안 정책 적용
- 즉각적 권한 회수: 퇴사자나 역할 변경 시 IdP에서 한 번에 접근 차단
- 제로 트러스트 초석: "Never Trust, Always Verify" 원칙의 기반
2. SSO 구현 아키텍처
SSO 시스템은 환경에 따라 세 가지 모델로 나뉜다.
| 모델 | 방식 | 적합 환경 |
|---|---|---|
| 에이전트 기반 | 개별 서버에 에이전트 설치 | 레거시 환경 |
| 에이전트리스 | API Gateway / Reverse Proxy로 트래픽 제어 | 클라우드 네이티브 |
| 아이덴티티 페더레이션 | 도메인 간 아이덴티티 공유 | B2B 협업, 멀티 조직 |
3. 기술 표준 비교: SAML vs OAuth 2.0 vs OIDC
| 항목 | SAML 2.0 | OAuth 2.0 | OIDC |
|---|---|---|---|
| 목적 | 인증(Authentication) | 인가(Authorization) | 인증 + 인가 |
| 데이터 형식 | XML (Assertion) | JSON | JSON (JWT) |
| 토큰 | SAML Assertion | Access Token | ID Token + Access Token |
| 주요 사용처 | 엔터프라이즈 웹앱 | API 권한 위임 | 모바일, SPA, MSA |
| 복잡도 | 높음 | 중간 | 낮음 |
**OIDC(OpenID Connect)**는 OAuth 2.0 위에 아이덴티티 계층을 얹어 JWT 형태로 가볍게 인증을 처리하며, 현대 마이크로서비스 환경의 사실상 표준이다.
4. Keycloak 핵심 아키텍처
Keycloak은 SAML, OAuth 2.0, OIDC 표준을 모두 지원하는 오픈소스 IAM 솔루션이다. Red Hat이 지원하며 CNCF 인큐베이팅 프로젝트다.
4.1 핵심 계층 구조
Keycloak Instance
└── Realm (논리적 격리 단위, 테넌트)
├── Clients (인증을 요청하는 어플리케이션)
├── Users (사용자)
├── Groups (사용자 그룹)
├── Roles (Realm Roles + Client Roles)
├── Identity Providers (외부 IdP 연동)
├── Authentication Flows (인증 흐름 정의)
└── Authorization Services (세밀한 인가 정책)
- Realm: 독립된 테넌트 공간.
masterRealm은 관리용, 실제 서비스는 별도 Realm 생성 - Client: OAuth/OIDC 클라이언트 어플리케이션 (Confidential / Public / Bearer-only)
- Roles & Groups: 권한 관리의 핵심. Realm Role은 전체 범위, Client Role은 특정 클라이언트 범위
4.2 기술 스택
| 컴포넌트 | 역할 |
|---|---|
| Quarkus | 고성능 Java 프레임워크 기반 런타임 |
| Infinispan | 인메모리 분산 캐시 (세션, 토큰 동기화) |
| PostgreSQL (등) | 사용자, 정책 등 영구 데이터 저장 |
5. 프로덕션 배포
5.1 Docker 배포
개발 모드 (빠른 시작, 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
프로덕션 모드 (TLS + PostgreSQL 필수):
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 배포
# CRD 및 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 매니페스트:
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 프로덕션 설정
# 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) 플로우
6.1 OIDC 엔드포인트
모든 엔드포인트는 Well-Known URL에서 자동 탐색 가능하다.
GET /realms/{realm}/.well-known/openid-configuration
| 엔드포인트 | 경로 |
|---|---|
| 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
웹 애플리케이션과 SPA/모바일에 권장되는 표준 플로우다.
1단계 - 인가 요청 (브라우저 리다이렉트):
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
2단계 - 토큰 교환 (서버 사이드):
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는 코드 가로채기 공격을 방지한다. code_verifier는 클라이언트가 생성한 랜덤 문자열이고, code_challenge는 그 SHA256 해시다. 토큰 교환 시 원본 code_verifier를 제출하여 검증한다.
6.3 Client Credentials Flow
서비스 간(M2M) 인증에 사용한다.
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 클라이언트에는 내장 Service Account가 있다. Clients > my-service > Service Account Roles에서 역할을 할당한다.
6.4 Client 타입 선택 가이드
| 타입 | 시크릿 | 사용처 |
|---|---|---|
| Confidential | 있음 | 서버 사이드 웹앱, 백엔드 서비스 |
| Public | 없음 | SPA, 모바일 앱 (PKCE 필수) |
| Bearer-only | 검증만 | REST API 서비스 (Resource Server) |
7. 인가(Authorization) 체계
7.1 RBAC (Role-Based Access Control)
역할 기반 접근 제어는 사용자에게 역할을 할당하고, 역할에 따라 접근을 허용/거부한다.
- Realm Role: 전체 Realm 범위 (예:
admin,user) - Client Role: 특정 클라이언트 범위 (예:
my-app:editor) - Composite Role: 여러 역할을 묶은 상위 역할
7.2 ABAC (Attribute-Based Access Control)
속성 기반 접근 제어는 사용자의 IP, 접속 시간, 부서 정보 등을 종합하여 동적으로 판단한다.
Keycloak Authorization Services는 다양한 정책 타입을 지원한다.
| 정책 타입 | 설명 |
|---|---|
| Role-based | Realm/Client 역할 평가 |
| Group-based | 그룹 멤버십 평가 |
| Time-based | 시간대/기간 제한 |
| Client-based | 요청 클라이언트 제한 |
| JavaScript | 커스텀 JS 조건 |
| Regex | 패턴 매칭 |
| Aggregated | 여러 정책 조합 |
7.3 권한 모델
"X(사용자)가 Y(액션)를 Z(리소스)에 수행할 수 있는가?"
# RPT(Requesting Party Token) 요청으로 권한 확인
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"
결정 전략:
- Affirmative: 하나의 정책이라도 허용하면 접근 허용
- Unanimous: 모든 정책이 허용해야 접근 허용
8. Identity Brokering과 Social Login
Keycloak은 외부 IdP와의 연동을 기본 지원한다.
8.1 지원 IdP
| 유형 | 지원 목록 |
|---|---|
| Social Login | Google, GitHub, Facebook, Microsoft, LinkedIn, GitLab 등 |
| 프로토콜 기반 | OIDC v1.0, OAuth 2.0, SAML 2.0 |
8.2 Google IdP 설정 예시
- Google Cloud Console에서 OAuth 2.0 자격증명 생성
- Redirect URI:
https://auth.example.com/realms/{realm}/broker/google/endpoint - Keycloak Admin Console > Identity Providers > Google 추가
- Client ID와 Client Secret 입력
8.3 LDAP/Active Directory 연동
User Federation을 통해 LDAP과 연동할 수 있다.
| 설정 | Active Directory | 일반 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 |
동기화 모드:
- READ_ONLY: LDAP에서 읽기만 (가장 안전)
- WRITABLE: Keycloak에서 LDAP 속성 수정 가능
- UNSYNCED: 변경사항을 Keycloak 로컬에만 저장
9. 어플리케이션 연동 실무
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],
})
Keycloak Client 설정에서 Redirect URI를 등록한다:
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',
})
// Authorization Code Flow + PKCE로 초기화
const authenticated = await keycloak.init({
onLoad: 'login-required',
pkceMethod: 'S256',
checkLoginIframe: false,
})
// API 호출 시 토큰 사용
const response = await fetch('/api/data', {
headers: {
Authorization: `Bearer ${keycloak.token}`,
},
})
// 토큰 만료 전 자동 갱신
keycloak.onTokenExpired = () => {
keycloak.updateToken(30).catch(() => keycloak.login())
}
// 역할 확인
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())
// 인증 필수 엔드포인트
app.get('/api/protected', keycloak.protect(), (req, res) => {
res.json({ message: 'Authenticated!' })
})
// 특정 역할 필수
app.get('/api/admin', keycloak.protect('realm:admin'), (req, res) => {
res.json({ message: 'Admin access granted' })
})
app.listen(3000)
10. MSA 환경의 Token Relay 패턴
마이크로서비스 아키텍처에서 API Gateway가 Keycloak과 연동하여 JWT 토큰을 획득한 후, 내부 서비스로 요청을 전달하는 패턴이다.
[Client] → [API Gateway] → [Keycloak] (토큰 획득/검증)
↓
Authorization: Bearer <JWT>
↓
[Service A] → [Service B] → [Service C]
각 서비스가 IdP와 직접 통신할 필요 없이 JWT의 서명만 로컬에서 검증하므로 뛰어난 성능과 보안성을 확보할 수 있다. Keycloak의 JWKS 엔드포인트(/protocol/openid-connect/certs)에서 공개키를 가져와 캐싱하면 된다.
11. Admin REST API 활용
11.1 토큰 획득
# 패스워드 방식
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 방식 (자동화 권장)
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 사용자 관리
# 사용자 생성
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
}]
}'
# 사용자 검색
curl -X GET \
"https://auth.example.com/admin/realms/myrealm/users?username=johndoe" \
-H "Authorization: Bearer $ACCESS_TOKEN"
# 역할 할당
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. 고가용성(HA) 아키텍처
12.1 분산 아키텍처
┌─────────────┐
│ Load Balancer│
└──────┬──────┘
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│Keycloak 1│ │Keycloak 2│ │Keycloak 3│
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ Infinispan │ (세션 동기화) │
└──────────────┴───────────────┘
│
┌──────┴──────┐
│ PostgreSQL │
│ (공유 DB) │
└─────────────┘
- 정적 데이터 (사용자, 정책): 공유 DB (PostgreSQL)에 저장
- 동적 데이터 (세션, 토큰): Infinispan 분산 캐시로 노드 간 동기화
12.2 Kubernetes Health Probe 설정
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 복제 (외부 Infinispan)
지리적으로 분리된 데이터센터 간 동기화가 필요한 경우:
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. 모니터링 및 관측성
13.1 Prometheus 메트릭
# prometheus.yml
scrape_configs:
- job_name: 'keycloak'
metrics_path: '/metrics'
scheme: https
static_configs:
- targets: ['keycloak-host:9000']
이벤트 메트릭 활성화:
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 대시보드
Keycloak 공식 Grafana 대시보드를 제공한다.
| 대시보드 | 용도 |
|---|---|
keycloak-troubleshooting-dashboard.json | SLI 및 진단 분석 |
keycloak-capacity-planning-dashboard.json | 부하 추정, 로그인 플로우 분석 |
다운로드: github.com/keycloak/keycloak-grafana-dashboard
14. 마무리
성공적인 SSO 구축을 위한 핵심 요약이다.
- 프로토콜: OIDC를 기본으로 선정하고, 레거시 엔터프라이즈 앱에만 SAML 적용
- 인가 체계: RBAC으로 기본 구조를 잡고, 세밀한 제어가 필요한 경우 ABAC 결합
- 고가용성: 최소 3개 노드 + Infinispan 분산 캐시 + 공유 DB 구성
- 어플리케이션 연동: Spring Boot는 Resource Server JWT 검증, SPA는 keycloak-js + PKCE, Next.js는 Auth.js 활용
- 모니터링: Prometheus 메트릭 + Grafana 대시보드로 로그인 실패율, 토큰 발급량 등 추적
Keycloak은 이 모든 것을 오픈소스로 제공하는 차세대 디지털 아이덴티티 거버넌스의 핵심이다.
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
The Definitive Guide to Next-Generation Digital Identity, SSO, and Keycloak in Practice
- 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
- Quiz
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
Quiz
Q1: What is the main topic covered in "The Definitive Guide to Next-Generation Digital Identity,
SSO, and Keycloak in Practice"?
Compare SSO architectures and SAML/OAuth2/OIDC standards, then dive into Keycloak production deployment, authentication flows, RBAC/ABAC authorization, LDAP integration, Spring Boot/Next.js integration, and high availability configuration at a production-ready level.
Q2: What is 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.
Q3: Describe the SSO Implementation Architecture.
SSO systems are divided into three models depending on the environment.
Q4: What are the key differences in Technical Standard Comparison: SAML vs OAuth 2.0 vs OIDC?
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.
Q5: Describe the 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 Realm: An isolated tenant
space.