1. クライアントポリシー
クライアント ポリシーは有効なフレームワークですセキュリティ要件を強制するクライアントに自動的にインストールされます。各クライアントの設定を手動で確認する代わりに、Keycloak が自動的に検証して適用するポリシーを定義します。
1.1 クライアント ポリシーが必要なのはなぜですか?
一貫性: すべてのクライアントが同じセキュリティ標準に準拠していることを確認します。
オートメーション: 準拠していないリクエストを自動的に拒否します
コンプライアンス: 業界標準の強制 (FAPI、PSD2、オープン バンキング)
ガバナンス: クライアントの登録と構成を制御します
1.2 アーキテクチャ: プロファイル、条件、エグゼキュータ
クライアント ポリシーには、次の 3 つの主要コンポーネントが含まれます。
┌─────────────────────────────────────────────────┐
│ 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 │ │ │ • ... │ │ │
│ └──────────────┘ │ └────────────────┘ │ │
│ └──────────────────────┘ │
└─────────────────────────────────────────────────┘
| 材料 | 説明する | 例えば |
|---|---|---|
| プロフィール | エグゼキュータのセット — 「何を強制するか」を定義します | fapi-2-セキュリティプロファイル |
| 状態 | 条件によって、どのクライアントが影響を受けるかが決まります - 「誰に対して強制するか」 | クライアントには役割があるファピクライアント |
| 執行者 | 特定の施行ロジック - 「施行方法」 | PKCE S256 が必要です |
1.3 クライアントプロファイルの作成
入力レルム設定 → クライアントポリシー→タブプロフィール
クリッククライアントプロファイルの作成
入力名前そして説明
クリック保存→プロフィールを開く→クリック実行者の追加
1.4 利用可能なエグゼキュータ
| 執行者 | 説明する | パラメータ |
|---|---|---|
| セキュアクライアント認証システム | 特定の認証方法が必要 | 許可された認証子: client-secret、client-jwt、client-x509 |
| PKCE 執行者 | PKCEが必要です | オーグメント: ON (クライアントが不足している場合は自動的に追加) |
| 安全な署名アルゴリズム | 安全なアルゴリズムのみが許可されます | デフォルト: RS256、ES256、PS256 |
| 署名付き JWT の安全な署名アルゴリズム | クライアント JWT 認証のアルゴリズム | PS256、ES256(RS256は不可) |
| 鍵の所有者執行者 | 必要なトークン バインディング (mTLS または DPoP) | 自動構成: オン |
| DPoP 証明検証者 | トークンリクエストで DPoP 証明を要求する | |
| 機密クライアント執行者 | 機密クライアントのみが許可されます | |
| 同意が必要です | 必須の同意画面 | |
| フルスコープ無効 | フルスコープマッピングをオフにする | |
| 暗黙的な許可を拒否する | 暗黙的なフローは許可されません | |
| リソース所有者のパスワード認証情報の付与を拒否する | ROPC は許可されません | |
| セキュア リダイレクト URI エンフォーサ | リダイレクト URI を検証する | HTTPS が必要、ワイルドカードなし |
| 安全なリクエストオブジェクト | 必須の JAR (JWT で保護された承認リクエスト) | |
| 安全な応答タイプ | 安全な応答タイプのみが許可されます | 許可: コード (トークンなし、id_token) |
| セキュアセッションエンフォーサ | セッション設定を強制する |
1.5 利用可能な条件
| 状態 | 説明する | 例えば |
|---|---|---|
| あらゆるクライアント | すべてのクライアントに適用されます | グローバルセキュリティポリシー |
| クライアントアクセスタイプ | クライアントのタイプに基づく (公開/機密) | すべてのパブリック クライアントに PKCE を適用する |
| クライアントの役割 | クライアントには特定の役割があります | クライアントには役割があるfapi準拠 |
| クライアントスコープ | クライアントは特定のスコープを使用します | クライアントリクエストのスコープ支払い |
| クライアント更新ソースグループ | ソースの作成/更新クライアントに基づく | 動的登録によって作成されたクライアント |
| クライアント更新コンテキスト | クライアント更新時のコンテキスト | 認可リクエスト、トークンリクエスト |
1.6 クライアントポリシーの作成
入力レルム設定 → クライアントポリシー→タブポリシー
クリッククライアントポリシーの作成
入力名前そして説明
もっと条件(影響を受けるクライアントを特定する)
もっとクライアントプロファイル(どのプロファイルが適用されるか)
# 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 実践的なポリシーの例
ポリシー 1: すべてのクライアントのベースライン セキュリティ
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
ポリシー 2: 金融 API の高セキュリティ
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 セキュリティ プロファイル
FAPI (金融グレード API) は、OpenID Foundation によって開発された一連の高度なセキュリティ標準であり、以下で広く使用されています。オープンバンキング, 決済サービス指令 2 (PSD2)、金融アプリケーションなど。
2.1 FAPI 2.0 ベースライン プロファイル
Keycloak は、FAPI 2.0 の組み込みプロファイルを提供します。
| リクエスト | 説明する |
|---|---|
| 認可コードフローのみ | 暗黙的および ROPC は許可されません |
| PKCE (S256) | すべてのクライアントに必須 |
| 機密クライアント | クライアント認証が必要です |
| 安全な署名アルゴリズム | PS256、ES256 (RS256なし) |
| 送信者制限付きトークン | DPoP または mTLS トークン バインディング |
| リダイレクト URI の完全一致 | ワイルドカードなし |
| HTTPSが必要です | すべてのエンドポイントに対して |
2.2 FAPI 2.0 高度なプロファイル (メッセージ署名)
ベースラインに加えて、アドバンスト プロファイルでは以下が追加されます。
PAR (プッシュされた認可リクエスト)— RFC 9126: リダイレクトする前にバックチャネル経由で認可リクエストを送信する
JAR (JWT で保護された承認リクエスト)— RFC 9101: JWT で署名された認証パラメーター
JARM (JWT で保護された認証応答モード): 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 KeycloakでFAPI 2.0を有効にする
入力レルム設定 → クライアントポリシー→タブプロフィール
キークロークが利用可能ですグローバルプロファイル:
fapi-2-セキュリティプロファイルfapi-2-メッセージ署名プロファイル
対応するプロファイルを使用してポリシーを作成する
コンプライアンスが必要なクライアントを選択するための条件の割り当て
# 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. クライアント シークレットのローテーション
クライアント シークレットのローテーションにより、クライアント シークレットを変更できますダウンタイムを引き起こさない— 古いシークレットは移行期間中もアクティブのままです。
3.1 クライアント シークレット ローテーションの構成
使用クライアントポリシー遺言執行者付きシークレットローテーション:
# 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
仕組み:
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 シークレットローテーションの導入
# 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. サービスアカウント
オンにした場合サービスアカウントの役割機密クライアントの場合、Keycloak は 1 つを作成しますサービスアカウントユーザー特にそのクライアントのために。このユーザーは、マシン間の操作においてクライアントを表します。
4.1 サービスアカウントのユーザー
# 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 サービスアカウントへの役割の割り当て
クライアント→タブを開くサービスアカウントの役割
クリック役割の割り当て
レルムの役割を選択するか、クライアントでフィルタリングして、クライアントの役割を割り当てます
# 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 サービスアカウントのベストプラクティス
最低限の特権: 各サービスに必要な役割のみを割り当てます
個別のクライアント: マイクロサービスごとに個別のクライアントを作成し、共有しないでください。
監査: イベント ログを有効にしてサービス アカウントのアクティビティを追跡します
トークンの寿命が短い: サービス アカウントのアクセス トークンは短くする必要があります (1 ~ 5 分)。
認証情報のローテーション: クライアント シークレット ローテーションまたは証明書ベースの認証を使用します。
5. 視聴者サポート
観客 (オード請求)決定どのリソースサーバーですか?アクセストークンは使用することを目的としています。これは、トークンが望ましくないサービスで使用されるのを防ぐための重要なセキュリティ メカニズムです。
5.1 問題点
# 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 ソリューション: オーディエンス プロトコル マッパー
もっとオーディエンスマッパークライアントまたはクライアント スコープに移動してリソース サーバーを追加しますオード:
# 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 オーディエンス解決マッパー
Keycloakには組み込み観客の決意マッパー (デフォルトのスコープ内)役割。役割) — 自動的に追加オードユーザーがクライアントの役割を持っているクライアントの場合:
# 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. 機密クライアント認証情報
6.1 クライアント ID とシークレット
最も単純な方法 — クライアントはリクエストで ID とシークレットを送信します。
# 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 署名付き JWT (private_key_jwt)
クライアントは秘密鍵を使用して JWT を作成および署名し、Keycloak に送信します。 Keycloakは登録された公開鍵/証明書を使用して検証します。
Keycloakでの設定:
クライアント → タブ資格 → クライアント認証子:
署名付き JWTクライアント証明書または 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...
クライアント アサーション JWT 構造:
{
"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証明書/相互TLS
クライアントはクライアント TLS 証明書 (相互 TLS — mTLS) を使用して認証します。これが最も安全な方法です。
構成:
クライアント → タブ資格 → クライアント認証子:
X.509証明書入力件名DNまたは証明書照合のパターン
Keycloakサーバーを構成してmTLSエンドポイントを有効にする
# 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"
mTLS と証明書にバインドされたトークンを組み合わせます。
# 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. 標準トークン交換 (RFC 8693)
トークン交換によりサービスが可能になりますトークンを交換する異なる権限または対象者を持つ新しいトークンを受け取るため。
7.1 使用例
代表団: サービス A は、ユーザーの「代理」としてサービス B を呼び出したいと考えています。アクセス トークンを交換して、オーディエンス = サービス B と新しいトークンを取得します。
なりすまし: 管理者は別のユーザーのように行動したいと考えています
トークンの種類の変換: SAML アサーションのアクセス トークンを交換します (またはその逆)。
7.2 トークン交換の構成
Keycloakのトークン交換はプレビュー機能— 有効にする必要があります:
# 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
権限を構成します。
開けるターゲットクライアント(トークンを交換したいクライアント) → タブ権限
オンにする許可が有効です
クリックトークン交換許可 → ソースクライアント交換を許可するポリシーを設定
# 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 委任と偽装
| モード | 説明する | トークンの要求 |
|---|---|---|
| 代表団 | サービス B は、サービス A がユーザーに代わって動作していることを認識しています | act.sub= サービス A、サブ= ユーザー |
| なりすまし | サービス B は知りません - トークンはそれを直接要求したユーザーと同一です | サブ= ユーザー (なし活動。活動) |
8. JWT 認可付与 (RFC 7523)
クライアントが使用できるようにしますJWT アサーションは信頼できる発行者によって発行されますユーザーの介入なしでアクセス トークンを取得します。
8.1 フロー
# 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 JWT 許可の構成
レルム設定 →キー→ 外部発行者の署名キーを追加
または構成アイデンティティプロバイダー外部発行者向け
クライアントが持っている必要があるサービスアカウントの役割有効になりました。有効
9. MCPサーバー用のKeycloakの構成
Model Context Protocol (MCP) サーバーは、OAuth 2.0 を使用してクライアントを認証します。 Keycloakが役割を果たす可能性がある認可サーバーMCP エコシステム向け。
9.1 MCP OAuth 2.0 フロー
MCP 仕様では、サーバー間およびクライアント間の認証に OAuth 2.0 が必要です。
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 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 MCP ホストのクライアントの作成
# 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 MCP サーバー (リソース サーバー) のクライアントの作成
# 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 MCP 操作のスコープの作成
# 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 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 MCP マルチサーバーのトークン交換
MCP ホストが多くの異なる MCP サーバーを呼び出す必要がある場合は、トークン交換を使用して各サーバーのトークンを取得します。
# 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 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. 練習問題
ラボ 1: クライアント ポリシー — ベースライン セキュリティ
クライアントプロファイルの作成
ベースラインセキュリティエグゼキュータを使用: 暗黙的な許可の拒否、PKCE エンフォーサ、安全な署名アルゴリズムクライアントポリシーの作成
ベースラインを強制する条件付きあらゆるクライアントテスト: 新しいクライアントを作成し、PKCE なしでトークンを要求しようとします → 拒否されました
テスト: 暗黙的なフローをオンにしてみてください → 拒否されました
ラボ 2: FAPI 2.0 への準拠
組み込みの FAPI 2.0 セキュリティ プロファイルを使用してクライアント プロファイルを作成する
ロールを持つクライアントにのみ適用されるポリシーを作成する
ファピクライアント署名付き JWT 認証を使用して機密クライアントを作成する
PAR + PKCE + DPoP を使用して完全な認証フローをテストする
ラボ 3: クライアント シークレットのローテーション
Secret Rotation executor を構成します (有効期限: 60 秒、猶予期間: テスト用に 30 秒)
機密クライアントの作成 → シークレットAを記録
60秒待つ → シークレットを再生成 → シークレットBを記録
検証: シークレット A は猶予期間 (30 秒) の間アクティブのままです。
検証: 猶予期間の後、シークレット B のみがアクティブになります
ラボ 4: サービス アカウント + トークン交換
3 つのクライアントを作成します。
フロントエンドアプリ(公共)、APIゲートウェイ(機密 + サービス アカウント)、決済サービス(機密)ユーザーは次の方法でログインします
フロントエンドアプリ→ アクセストークンを受け取るAPIゲートウェイフロントエンドからトークンを受け取り、新しいトークンと交換します決済サービス確認: 新しいトークンが利用可能です
aud: 支払いサービスそしてact.sub: API ゲートウェイ
ラボ 5: MCP サーバーの構成
レルムの作成
mcp-デモクライアントを作成します。
mcp-ホスト(機密)、mcp-ツール-サーバー(機密)クライアント スコープを作成します。
mcp:ツール:読み取り,mcp:ツール:実行オーディエンス マッパーを構成する
mcp-ホスト→ 視聴者 =mcp-ツール-サーバークライアント認証情報フローでトークンを取得する
トークンの内容を確認します: 対象者、スコープ、権限
MCP サーバーのシミュレートは、JWKS エンドポイントを使用してトークンを検証します
ラボ 6: 署名付き JWT クライアント認証
RSA キー ペアの生成 (
オープンSSL)オーセンティケータを使用して機密クライアントを作成 =
署名付き JWT証明書をKeycloakにアップロードする
client_assertion JWT を作成して署名するスクリプトを作成します。
トークンをリクエストする
クライアントアサーションの代わりにクライアントシークレット受け取ったトークンを確認する