1. Client Policies
Client Policies là framework cho phép enforce security requirements lên clients một cách tự động. Thay vì phải kiểm tra thủ công cấu hình từng client, bạn định nghĩa policies để Keycloak tự động validate và enforce.
1.1 Tại sao cần Client Policies?
Consistency: Đảm bảo tất cả clients tuân thủ cùng security standards
Automation: Tự động reject requests không tuân thủ
Compliance: Enforce industry standards (FAPI, PSD2, Open Banking)
Governance: Kiểm soát client registration và configuration
1.2 Architecture: Profiles, Conditions, Executors
Client Policies gồm 3 thành phần chính:
┌─────────────────────────────────────────────────┐
│ Client Policy │
│ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Conditions │ │ Profiles │ │
│ │ (Khi nào?) │────>│ (Áp dụng gì?) │ │
│ │ │ │ │ │
│ │ • Client Role │ │ ┌────────────────┐ │ │
│ │ • Client Scope│ │ │ Executors │ │ │
│ │ • Any Client │ │ │ (Làm gì?) │ │ │
│ │ • Client │ │ │ │ │ │
│ │ Access Type │ │ │ • PKCE Enforcer│ │ │
│ │ • Client │ │ │ • Secure Alg │ │ │
│ │ Update │ │ │ • DPoP Verify │ │ │
│ │ Source │ │ │ • ... │ │ │
│ └──────────────┘ │ └────────────────┘ │ │
│ └──────────────────────┘ │
└─────────────────────────────────────────────────┘
| Thành phần | Mô tả | Ví dụ |
|---|---|---|
| Profile | Tập hợp các Executors — định nghĩa "enforce cái gì" | fapi-2-security-profile |
| Condition | Điều kiện xác định client nào bị ảnh hưởng — "enforce cho ai" | Clients có role fapi-client |
| Executor | Logic enforcement cụ thể — "enforce như thế nào" | Bắt buộc PKCE S256 |
1.3 Tạo Client Profile
Vào Realm Settings → Client Policies → tab Profiles
Click Create client profile
Nhập Name và Description
Click Save → mở profile → click Add executor
1.4 Executors có sẵn
| Executor | Mô tả | Tham số |
|---|---|---|
| Secure Client Authenticator | Bắt buộc phương thức authentication cụ thể | Allowed authenticators: client-secret, client-jwt, client-x509 |
| PKCE Enforcer | Bắt buộc PKCE | Augment: ON (tự thêm nếu client thiếu) |
| Secure Signing Algorithm | Chỉ cho phép algorithms an toàn | Default: RS256, ES256, PS256 |
| Secure Signing Algorithm for Signed JWT | Algorithm cho client JWT auth | PS256, ES256 (không cho phép RS256) |
| Holder-of-Key Enforcer | Bắt buộc token binding (mTLS hoặc DPoP) | Auto-configure: ON |
| DPoP Proof Verifier | Bắt buộc DPoP proof trong token requests | |
| Confidential Client Enforcer | Chỉ cho phép confidential clients | |
| Consent Required | Bắt buộc consent screen | |
| Full Scope Disabled | Tắt full scope mapping | |
| Reject Implicit Grant | Không cho phép implicit flow | |
| Reject Resource Owner Password Credentials Grant | Không cho phép ROPC | |
| Secure Redirect URIs Enforcer | Validate redirect URIs | Require HTTPS, không wildcard |
| Secure Request Object | Bắt buộc JAR (JWT-Secured Authorization Request) | |
| Secure Response Type | Chỉ cho phép response types an toàn | Allowed: code (không token, id_token) |
| Secure Session Enforcer | Enforce session settings |
1.5 Conditions có sẵn
| Condition | Mô tả | Ví dụ |
|---|---|---|
| Any Client | Áp dụng cho tất cả clients | Global security policy |
| Client Access Type | Dựa trên client type (public/confidential) | Enforce PKCE cho tất cả public clients |
| Client Roles | Client có role cụ thể | Clients có role fapi-compliant |
| Client Scopes | Client sử dụng scope cụ thể | Clients request scope payment |
| Client Update Source Groups | Dựa trên nguồn tạo/update client | Clients tạo qua Dynamic Registration |
| Client Update Context | Context khi client được update | Authorization request, Token request |
1.6 Tạo Client Policy
Vào Realm Settings → Client Policies → tab Policies
Click Create client policy
Nhập Name và Description
Thêm Conditions (xác định client nào bị ảnh hưởng)
Thêm Client Profiles (profile nào apply)
# Ví dụ: Tạo policy enforce PKCE cho tất cả public clients
Profile: pkce-required-profile
Executors:
- PKCE Enforcer
Augment: ON (auto-add PKCE nếu client không gửi)
Policy: enforce-pkce-for-public
Conditions:
- Client Access Type: public
Profiles:
- pkce-required-profile
1.7 Ví dụ Policy thực tế
Policy 1: Baseline Security cho tất cả clients
Profile: baseline-security Executors: - Reject Implicit Grant - Reject Resource Owner Password Credentials Grant - PKCE Enforcer (S256) - Secure Signing Algorithm (RS256, ES256, PS256)
Policy: baseline-all-clients Conditions: - Any Client Profiles: - baseline-security
Policy 2: High-Security cho Financial APIs
Profile: financial-api-profile Executors: - Confidential Client Enforcer - Holder-of-Key Enforcer (mTLS hoặc DPoP) - Secure Client Authenticator (private_key_jwt, client-x509) - Secure Request Object Required - Consent Required - Secure Redirect URIs Enforcer (HTTPS only)
Policy: financial-api-policy Conditions: - Client Scopes: fapi-scope Profiles: - financial-api-profile
2. FAPI 2.0 Security Profile
FAPI (Financial-grade API) là bộ tiêu chuẩn bảo mật cao do OpenID Foundation phát triển, được sử dụng rộng rãi trong Open Banking, Payment Services Directive 2 (PSD2), và các ứng dụng tài chính.
2.1 FAPI 2.0 Baseline Profile
Keycloak cung cấp sẵn built-in profiles cho FAPI 2.0:
| Yêu cầu | Mô tả |
|---|---|
| Authorization Code Flow only | Không cho phép implicit, ROPC |
| PKCE (S256) | Bắt buộc cho tất cả clients |
| Confidential Client | Bắt buộc client authentication |
| Secure Signing Algorithms | PS256, ES256 (không RS256) |
| Sender-constrained tokens | DPoP hoặc mTLS token binding |
| Redirect URI exact match | Không wildcard |
| HTTPS required | Cho tất cả endpoints |
2.2 FAPI 2.0 Advanced Profile (Message Signing)
Ngoài baseline, Advanced Profile thêm:
PAR (Pushed Authorization Requests) — RFC 9126: gửi authorization request qua backchannel trước khi redirect
JAR (JWT-Secured Authorization Request) — RFC 9101: authorization parameters được ký trong JWT
JARM (JWT-Secured Authorization Response Mode): authorization response được ký trong JWT
# PAR request — gửi authorization params qua backchannel
POST /realms/my-realm/protocol/openid-connect/ext/par/request
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)
response_type=code&
client_id=my-fapi-client&
redirect_uri=https://myapp.com/callback&
scope=openid payments&
state=random-state&
code_challenge=code_challenge_value&
code_challenge_method=S256
# Response
{
"request_uri": "urn:ietf:params:oauth:request_uri:abc123",
"expires_in": 60
}
# Authorization request chỉ chứa request_uri
GET /realms/my-realm/protocol/openid-connect/auth?
client_id=my-fapi-client&
request_uri=urn:ietf:params:oauth:request_uri:abc123
2.3 Bật FAPI 2.0 trong Keycloak
Vào Realm Settings → Client Policies → tab Profiles
Keycloak cung cấp sẵn Global Profiles:
fapi-2-security-profilefapi-2-message-signing-profile
Tạo Policy sử dụng profile tương ứng
Gán Condition để chọn clients cần compliance
# Ví dụ: Enforce FAPI 2.0 cho clients có scope "fapi"
Policy: fapi-2-enforcement
Conditions:
- Client Scopes: fapi
Profiles:
- fapi-2-security-profile # Built-in global profile
- fapi-2-message-signing-profile # Thêm nếu cần message signing
3. Client Secret Rotation
Client Secret Rotation cho phép thay đổi client secret không gây downtime — secret cũ vẫn hoạt động trong một khoảng thời gian chuyển tiếp.
3.1 Cấu hình Client Secret Rotation
Sử dụng Client Policy với executor Secret Rotation:
# Tạo Profile với Secret Rotation executor
Profile: secret-rotation-profile
Executors:
- Secret Rotation
Secret Expiration: 2592000 # 30 ngày (tính bằng giây)
Rotated Secret Expiration: 604800 # Grace period: 7 ngày
Remain Expiration: 604800 # Thời gian cảnh báo trước khi hết hạn
Cách hoạt động:
Timeline:
┌──────────────────────────────────────────────────────────┐
│ Ngày 0 Ngày 23 Ngày 30 Ngày 37 │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ Secret A Cảnh báo Secret B Secret A │
│ created sắp hết hạn created + active hết hạn │
│ Secret A vẫn hoàn toàn│
│ hoạt động │
│ (grace period) │
└──────────────────────────────────────────────────────────┘
Khoảng grace period (Ngày 30-37):
- Secret B: active (primary)
Secret A: vẫn valid (rotated secret, grace) → Ứng dụng có 7 ngày để chuyển sang Secret B
3.2 Triển khai Secret Rotation
# 1. Lấy current secret CURRENT_SECRET=$(curl -s -X GET \ "$KC_URL/admin/realms/my-realm/clients/$CLIENT_UUID/client-secret" \ -H "Authorization: Bearer $ADMIN_TOKEN" | jq -r '.value')2. Rotate secret — regenerate new secret
curl -s -X POST
"$KC_URL/admin/realms/my-realm/clients/$CLIENT_UUID/client-secret"
-H "Authorization: Bearer $ADMIN_TOKEN"3. Lấy new secret
NEW_SECRET=$(curl -s -X GET
"$KC_URL/admin/realms/my-realm/clients/$CLIENT_UUID/client-secret"
-H "Authorization: Bearer $ADMIN_TOKEN" | jq -r '.value')4. Update ứng dụng với new secret
Trong grace period, cả current và new secret đều hoạt động
4. Service Accounts
Khi bật Service accounts roles cho confidential client, Keycloak tạo một service account user đặc biệt cho client đó. User này đại diện cho client trong các machine-to-machine operations.
4.1 Service Account User
# Service account user naming convention Username: service-account-{client-id} # Ví dụ: service-account-my-backend-serviceService account user có các đặc điểm:
- Không có password (authenticate bằng client credentials)
- Có thể gán realm roles và client roles
- Có thể thêm user attributes
- Xuất hiện trong Users list (với filter service accounts)
4.2 Gán Roles cho Service Account
Mở client → tab Service account roles
Click Assign role
Chọn realm roles hoặc filter by clients để gán client roles
# Admin CLI: Gán roles
# Gán realm role
bin/kcadm.sh add-roles -r my-realm \
--uusername service-account-my-backend-service \
--rolename realm-admin
# Gán client role từ client khác
bin/kcadm.sh add-roles -r my-realm \
--uusername service-account-my-backend-service \
--cclientid realm-management \
--rolename manage-users
# REST API: Gán role
# Lấy service account user
SA_USER=$(curl -s -X GET \
"$KC_URL/admin/realms/my-realm/clients/$CLIENT_UUID/service-account-user" \
-H "Authorization: Bearer $ADMIN_TOKEN")
SA_USER_ID=$(echo $SA_USER | jq -r '.id')
# Gán realm role
ROLE_ID=$(curl -s -X GET \
"$KC_URL/admin/realms/my-realm/roles/admin" \
-H "Authorization: Bearer $ADMIN_TOKEN" | jq -r '.id')
curl -s -X POST \
"$KC_URL/admin/realms/my-realm/users/$SA_USER_ID/role-mappings/realm" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '[{"id":"'$ROLE_ID'","name":"admin"}]'
4.3 Best Practices cho Service Accounts
Least privilege: Chỉ gán roles cần thiết cho mỗi service
Separate clients: Tạo client riêng cho mỗi microservice, không dùng chung
Audit: Enable events logging để track service account activities
Short token lifespan: Access token cho service accounts nên ngắn (1-5 phút)
Rotate credentials: Sử dụng Client Secret Rotation hoặc certificate-based auth
5. Audience Support
Audience (aud claim) xác định resource server nào access token được dự định sử dụng. Đây là cơ chế bảo mật quan trọng để ngăn token được sử dụng ở service không mong muốn.
5.1 Vấn đề
# Mặc định, access token chỉ có aud = client-id đã request { "aud": "my-frontend-app", // ← chỉ có client đã request "azp": "my-frontend-app" }Resource Server (my-api-service) verify token:
→ aud không chứa "my-api-service"
→ REJECT! (nếu resource server validate audience)
5.2 Giải pháp: Audience Protocol Mapper
Thêm Audience Mapper vào client hoặc client scope để thêm resource server vào aud:
# Cách 1: Thêm Audience Mapper trực tiếp vào client
Client: my-frontend-app → Client scopes → Dedicated scope → Add mapper
Mapper Type: Audience
Name: api-audience
Included Client Audience: my-api-service
Included Custom Audience: (trống)
Add to ID token: OFF
Add to access token: ON
# Cách 2: Tạo Client Scope chứa Audience Mapper
Client Scope: api-access
Mapper: Audience → my-api-service
Gán scope cho frontend client
# Kết quả trong access token:
{
"aud": ["my-frontend-app", "my-api-service"],
"azp": "my-frontend-app"
}
5.3 Audience Resolve Mapper
Keycloak có built-in Audience Resolve mapper (trong default scope roles) — tự động thêm aud cho clients mà user có client roles:
# Nếu user có role "app-admin" của client "my-api-service"
# → Audience Resolve tự động thêm "my-api-service" vào aud
{
"aud": ["my-frontend-app", "my-api-service"],
"resource_access": {
"my-api-service": {
"roles": ["app-admin"]
}
}
}
6. Confidential Client Credentials
6.1 Client ID and Secret
Phương thức đơn giản nhất — client gửi ID và secret trong request:
# Cách 1: Form parameter
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&
client_id=my-client&
client_secret=my-secret
# Cách 2: HTTP Basic Authentication
POST /token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)
grant_type=client_credentials
6.2 Signed JWT (private_key_jwt)
Client tạo và ký JWT bằng private key, gửi đến Keycloak. Keycloak verify bằng public key/certificate đã đăng ký.
Cấu hình trong Keycloak:
Client → tab Credentials → Client Authenticator:
Signed JWTUpload client certificate hoặc JWKS URL
# Tạo key pair cho client
openssl genrsa -out client-private.pem 2048
openssl req -new -x509 -key client-private.pem -out client-cert.pem -days 365
# Upload client-cert.pem vào Keycloak client Credentials tab
# Token request với client_assertion
POST /realms/my-realm/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&
client_id=my-client&
client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&
client_assertion=eyJhbGciOiJSUzI1NiIs...
Client Assertion JWT structure:
{
"iss": "my-client", // Client ID
"sub": "my-client", // Client ID
"aud": "http://localhost:8080/realms/my-realm", // Token endpoint
"iat": 1711800000,
"exp": 1711800060, // Short-lived (60s)
"jti": "unique-jwt-id" // Unique ID
}
6.3 X.509 Certificate / Mutual TLS
Client xác thực bằng client TLS certificate (Mutual TLS — mTLS). Đây là phương thức bảo mật nhất.
Cấu hình:
Client → tab Credentials → Client Authenticator:
X.509 CertificateNhập Subject DN hoặc pattern cho certificate matching
Cấu hình Keycloak server enable mTLS endpoint
# Keycloak mTLS configuration (quarkus)
# conf/keycloak.conf hoặc environment variables
KC_HTTPS_CLIENT_AUTH=request
KC_HTTPS_KEY_STORE_FILE=/opt/keycloak/certs/server-keystore.p12
KC_HTTPS_TRUST_STORE_FILE=/opt/keycloak/certs/truststore.p12
# Client gọi token endpoint với client certificate
curl -s -X POST \
"https://localhost:8443/realms/my-realm/protocol/openid-connect/token" \
--cert client-cert.pem \
--key client-private.pem \
-d "grant_type=client_credentials" \
-d "client_id=my-mtls-client"
Kết hợp mTLS với certificate-bound tokens:
# Access token chứa certificate thumbprint { "cnf": { "x5t#S256": "sha256-thumbprint-of-client-certificate" } }Resource server verify:
1. Client gửi request với TLS client certificate
2. Resource server extract certificate thumbprint
3. So sánh với cnf.x5t#S256 trong access token
→ Nếu match → token hợp lệ + bound to correct client
7. Standard Token Exchange (RFC 8693)
Token Exchange cho phép một service exchange token để nhận token mới với quyền hạn hoặc audience khác.
7.1 Use Cases
Delegation: Service A muốn gọi Service B "nhân danh" user — exchange access token lấy token mới với audience = Service B
Impersonation: Admin muốn hoạt động như user khác
Token type conversion: Exchange access token lấy SAML assertion (hoặc ngược lại)
7.2 Cấu hình Token Exchange
Token Exchange trong Keycloak là preview feature — cần enable:
# Bật feature
bin/kc.sh start-dev --features=token-exchange
# Docker
docker run -e KC_FEATURES=token-exchange quay.io/keycloak/keycloak:26.2.4 start-dev
Cấu hình permissions:
Mở target client (client mà bạn muốn exchange token sang) → tab Permissions
Bật Permissions Enabled
Click token-exchange permission → cấu hình policy cho phép source client exchange
# Token Exchange request
POST /realms/my-realm/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
subject_token=USER_ACCESS_TOKEN&
subject_token_type=urn:ietf:params:oauth:token-type:access_token&
requested_token_type=urn:ietf:params:oauth:token-type:access_token&
audience=target-service&
client_id=source-service&
client_secret=SOURCE_SECRET
# Response — token mới cho target-service
{
"access_token": "new-token-for-target-service",
"token_type": "Bearer",
"expires_in": 300,
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token"
}
7.3 Delegation vs Impersonation
| Mode | Mô tả | Token claims |
|---|---|---|
| Delegation | Service B biết rằng Service A đang hành động nhân danh user | act.sub = Service A, sub = user |
| Impersonation | Service B không biết — token giống hệt user trực tiếp request | sub = user (không có act) |
8. JWT Authorization Grant (RFC 7523)
Cho phép client sử dụng một JWT assertion được cấp bởi trusted issuer để lấy access token mà không cần user interaction.
8.1 Flow
# External issuer (ví dụ: Azure AD, Google) cấp JWT cho client # Client gửi JWT đến Keycloak để exchange lấy Keycloak access tokenPOST /realms/my-realm/protocol/openid-connect/token Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer& assertion=eyJhbGciOiJSUzI1NiIs...& # JWT from external issuer client_id=my-client& client_secret=my-secret& scope=openid
8.2 Cấu hình JWT Grant
Realm Settings → Keys → thêm external issuer's signing key
Hoặc cấu hình Identity Provider cho external issuer
Client phải có Service accounts roles enabled
9. Cấu hình Keycloak cho MCP Servers
Model Context Protocol (MCP) servers sử dụng OAuth 2.0 để xác thực clients. Keycloak có thể đóng vai trò Authorization Server cho MCP ecosystem.
9.1 MCP OAuth 2.0 Flow
MCP specification yêu cầu OAuth 2.0 cho server-to-server và client-to-server authentication:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ MCP Host │ │ Keycloak │ │MCP Server│
│ (Client) │ │ (AuthZ) │ │(Resource)│
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
│ 1. Request │ │
│ auth info │ │
│───────────────────────────────>│
│ 2. Return │ │
│ auth metadata│ │
│<──────────────────────────────│
│ │ │
│ 3. Authorization Code Flow │
│ (hoặc Client Credentials) │
│───────────────>│ │
│ 4. Tokens │ │
│<───────────────│ │
│ │ │
│ 5. API call with access token │
│───────────────────────────────>│
│ 6. MCP Server validates token │
│ via Keycloak JWKS/Introspect│
│<──────────────────────────────│
9.2 Tạo Client cho MCP Host
# MCP Host client — ứng dụng AI/LLM kết nối tới MCP servers Client ID: mcp-host-app Client type: OpenID Connect Client authentication: ON (confidential)Capability Config: Standard flow: ON # Cho interactive MCP sessions Service accounts roles: ON # Cho automated MCP operations
Access Settings: Valid redirect URIs: http://localhost:3001/callback Web origins: http://localhost:3001
Advanced: PKCE Code Challenge Method: S256 Access Token Lifespan: 300 # 5 phút
9.3 Tạo Client cho MCP Server (Resource Server)
# MCP Server client — validate incoming tokens Client ID: mcp-tool-server Client type: OpenID Connect Client authentication: ON (confidential)Capability Config: Standard flow: OFF Service accounts roles: ON # Nếu MCP server cần gọi Keycloak APIs
MCP Server cấu hình JWT validation
Sử dụng Keycloak JWKS endpoint để verify access tokens
JWKS_URI: http://localhost:8080/realms/my-realm/protocol/openid-connect/certs ISSUER: http://localhost:8080/realms/my-realm
9.4 Tạo Scopes cho MCP Operations
# Tạo Client Scopes cho MCP permissions Client Scope: mcp:tools:read Type: Optional Description: Read access to MCP tools Protocol Mapper: Hardcoded claim Token Claim Name: mcp_permissions Claim Value: ["tools:read"]Client Scope: mcp:tools:execute Type: Optional Description: Execute MCP tools Protocol Mapper: Hardcoded claim Token Claim Name: mcp_permissions Claim Value: ["tools:execute"]
Client Scope: mcp:resources:read Type: Optional Description: Read MCP resources Protocol Mapper: Hardcoded claim Token Claim Name: mcp_permissions Claim Value: ["resources:read"]
Gán scopes cho MCP Host client
Client: mcp-host-app Default scopes: mcp:tools:read, mcp:resources:read Optional scopes: mcp:tools:execute
9.5 Audience Mapper cho MCP
# MCP Host client cần access token với audience = MCP Server Client: mcp-host-app → Client scopes → Dedicated scope → Add mapper Mapper Type: Audience Name: mcp-server-audience Included Client Audience: mcp-tool-server Add to access token: ONAccess token kết quả:
{ "iss": "http://localhost:8080/realms/my-realm", "sub": "user-or-service-account-id", "aud": ["mcp-host-app", "mcp-tool-server"], "azp": "mcp-host-app", "scope": "openid mcp:tools:read mcp:resources:read", "mcp_permissions": ["tools:read", "resources:read"] }
9.6 Token Exchange cho MCP Multi-Server
Khi MCP Host cần gọi nhiều MCP servers khác nhau, sử dụng Token Exchange để lấy token cho từng server:
# MCP Host có access token cho mcp-tool-server-1
# Cần access mcp-tool-server-2
POST /realms/my-realm/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
subject_token=CURRENT_ACCESS_TOKEN&
subject_token_type=urn:ietf:params:oauth:token-type:access_token&
audience=mcp-tool-server-2&
client_id=mcp-host-app&
client_secret=HOST_SECRET&
scope=mcp:tools:execute
9.7 Client Policy cho MCP
# Enforce security cho tất cả MCP clients Profile: mcp-security-profile Executors: - PKCE Enforcer (S256) - Confidential Client Enforcer - Secure Signing Algorithm (RS256, ES256) - Reject Implicit Grant - Reject Resource Owner Password Credentials Grant - Holder-of-Key Enforcer # DPoP cho high-security MCP operations
Policy: mcp-clients-policy Conditions: - Client Scopes: mcp:tools:read # Áp dụng cho clients request MCP scopes Profiles: - mcp-security-profile
10. Bài tập thực hành
Lab 1: Client Policies — Baseline Security
Tạo Client Profile
baseline-securityvới executors: Reject Implicit Grant, PKCE Enforcer, Secure Signing AlgorithmTạo Client Policy
enforce-baselinevới conditionAny ClientTest: Tạo client mới và thử request token không có PKCE → bị reject
Test: Thử bật Implicit flow → bị reject
Lab 2: FAPI 2.0 Compliance
Tạo Client Profile sử dụng built-in FAPI 2.0 Security Profile
Tạo Policy chỉ áp dụng cho clients có role
fapi-clientTạo confidential client với Signed JWT authentication
Test authorization flow đầy đủ với PAR + PKCE + DPoP
Lab 3: Client Secret Rotation
Cấu hình Secret Rotation executor (expiration: 60 giây, grace: 30 giây cho testing)
Tạo confidential client → ghi nhận secret A
Chờ 60 giây → regenerate secret → ghi nhận secret B
Verify: Secret A vẫn hoạt động trong grace period (30 giây)
Verify: Sau grace period, chỉ secret B hoạt động
Lab 4: Service Account + Token Exchange
Tạo 3 clients:
frontend-app(public),api-gateway(confidential + service account),payment-service(confidential)User đăng nhập qua
frontend-app→ nhận access tokenapi-gatewaynhận token từ frontend, exchange lấy token mới chopayment-serviceVerify: Token mới có
aud: payment-servicevàact.sub: api-gateway
Lab 5: MCP Server Configuration
Tạo realm
mcp-demoTạo clients:
mcp-host(confidential),mcp-tools-server(confidential)Tạo client scopes:
mcp:tools:read,mcp:tools:executeCấu hình Audience Mapper cho
mcp-host→ audience =mcp-tools-serverLấy token với Client Credentials flow
Verify token contents: audience, scopes, permissions
Simulate MCP Server validate token bằng JWKS endpoint
Lab 6: Signed JWT Client Authentication
Generate RSA key pair (
openssl)Tạo confidential client với authenticator =
Signed JWTUpload certificate vào Keycloak
Viết script tạo và ký client_assertion JWT
Request token với
client_assertionthay vìclient_secretVerify token nhận được