- Published on
Keycloak 26 アーキテクチャ徹底解説 — Realm、Client、Quarkus ランタイムの理解
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- はじめに
- Keycloak の歴史 — WildFly から Quarkus へ
- ドメインモデル — Realm、Client、User、Role、Group
- Confidential vs Public Client
- Client Scope — トークンの中身を組み立てる単位
- 認証フローエンジン — Browser Flow の内部構造
- データベーススキーマの概要
- kc.sh — Build と Start の二段階構造
- 設定方法 — 環境変数、conf ファイル、CLI
- Keycloak 26.x の新機能まとめ
- プロダクションチェックリスト
- トラブルシューティング — よくある問題と原因
- アンチパターン集
- おわりに
- 参考資料
はじめに
2026 年現在、Keycloak はオープンソース IAM(Identity and Access Management)分野で事実上の標準の地位を占めています。CNCF インキュベーティングプロジェクトとなって以降リリースサイクルが安定し、2026 年 5 月時点の最新バージョンは 26.6.2 です。passkeys のデフォルト化、AI エージェントアイデンティティ(non-human identity)、Zero Trust アーキテクチャといった 2026 年のトレンドの中で、Keycloak は FAPI 2.0 Security Profile の最終版サポート、JWT Authorization Grant、OAuth Client ID Metadata Document(CIMD)の実験的サポートなどで急速に進化しています。
しかし、Keycloak を「とりあえず起動して使う」レベルを超えてプロダクションで安定運用するには、内部アーキテクチャの理解が不可欠です。本記事では以下を扱います。
- WildFly から Quarkus への移行がもたらした変化
- realm、client、user、role、group で構成されるドメインモデル
- confidential vs public client と client scope の動作原理
- 認証フロー(browser flow)エンジンの内部構造
- データベーススキーマの概要
- kc.sh の build/start 二段階構造と最適化
- Keycloak 26.x の新機能とプロダクションチェックリスト
Keycloak の歴史 — WildFly から Quarkus へ
Keycloak は 2014 年に Red Hat で始まったプロジェクトで、当初は JBoss/WildFly アプリケーションサーバー上で動作する伝統的な Java EE アプリケーションでした。WildFly ベースのディストリビューションには次のような限界がありました。
- standalone.xml を中心とした複雑な XML 設定
- 遅い起動時間(数十秒)と大きなメモリフットプリント
- コンテナ/Kubernetes 環境との不整合(イミュータブルインフラのパラダイムと衝突)
- Java EE サブシステム全体を搭載した重いランタイム
2021 年に Keycloak.X プロジェクトとして始まった Quarkus 移行は、Keycloak 17(2022)でデフォルトのディストリビューションとなり、WildFly 版は 19 を最後に完全に削除されました。Quarkus ベース移行の核心はビルドタイム最適化です。
| 項目 | WildFly 版 | Quarkus 版 |
|---|---|---|
| 設定方式 | standalone.xml の編集 | conf ファイル、環境変数、CLI オプション |
| 起動時間 | 20 秒以上 | 数秒以内 |
| メモリ使用量 | 高い | 比較的低い |
| 拡張のデプロイ | EAR/WAR モジュール | providers ディレクトリに JAR |
| コンテナ親和性 | 低い | 非常に高い(公式イメージ最適化) |
| 設定変更の反映 | 再起動 | build 段階の再実行後に起動 |
Quarkus は CDI のワイヤリング、クラスパススキャン、設定パースといった作業を可能な限りビルドタイムに済ませます。そのため Keycloak も「ビルドオプション」と「ランタイムオプション」が明確に分離されており、これが後述する kc.sh build 段階の存在理由です。
ドメインモデル — Realm、Client、User、Role、Group
Keycloak のすべての概念は realm を頂点とするツリー構造として理解できます。
+--------------------------------------------------------------+
| Keycloak Server |
| |
| +--------------------+ +--------------------+ |
| | Realm: master | | Realm: production | |
| | (管理専用を推奨) | | | |
| +--------------------+ | +--------------+ | |
| | | Clients | | |
| | | - web-app | | |
| | | - api-gw | | |
| | | - cli-tool | | |
| | +--------------+ | |
| | +--------------+ | |
| | | Users | | |
| | | - alice |--+-- Groups |
| | | - bob | | Roles |
| | +--------------+ | |
| | +--------------+ | |
| | | Identity | | |
| | | Providers | | |
| | +--------------+ | |
| +--------------------+ |
+--------------------------------------------------------------+
Realm
realm はユーザー、クライアント、ロール、グループ、認証フロー、トークンポリシーをすべて含む分離単位です。テナント境界と考えると分かりやすいでしょう。運用観点で重要な原則は次のとおりです。
- master realm は Keycloak 自体の管理専用とし、アプリケーションユーザーは別の realm に置きます。
- realm の数が数百を超えるとキャッシュと起動時間に負担がかかるため、マルチテナンシーが必要なら organizations 機能(26 で正式化)をまず検討します。
- realm ごとにトークン有効期間、パスワードポリシー、ブルートフォース保護の設定が独立しています。
Client
client は Keycloak に認証を委任するアプリケーションまたはサービスです。OIDC 用語の Relying Party、SAML 用語の Service Provider に相当します。Web アプリ、SPA、モバイルアプリ、バックエンド API、CLI ツールのすべてが client として登録されます。
User、Role、Group
- user は realm に所属する認証主体です。credential(パスワード、OTP、passkey)、attribute、federated identity リンクを持ちます。
- role は権限の単位で、realm role と client role の二種類があります。client role は特定の client 名前空間に属します。
- group はユーザーの集合で階層構造を持つことができ、group に role をマッピングすると所属ユーザーが role を継承します。
- composite role は他の role を含む role で、乱用すると権限の追跡が困難になるため、group ベースのマッピングを優先するのが定石です。
role と group の使い分けの基準を整理すると次のようになります。
| 基準 | Role | Group |
|---|---|---|
| 意味 | 権限(permission)の表現 | 組織/ユーザー集合の表現 |
| 階層構造 | composite で擬似的に可能 | ネイティブサポート |
| トークンへの反映 | realm_access、resource_access クレーム | groups クレーム(protocol mapper が必要) |
| 推奨パターン | アプリケーションが検査する単位 | role を一括付与する運用単位 |
Confidential vs Public Client
OAuth 2.0 のクライアントタイプの区別は、Keycloak の設定では Client authentication トグルで表現されます。
| 項目 | Confidential Client | Public Client |
|---|---|---|
| シークレットの保管 | 可能(サーバーサイド) | 不可能(ブラウザ/モバイル) |
| 例 | Spring Boot バックエンド、API ゲートウェイ | SPA、ネイティブモバイルアプリ |
| クライアント認証 | client secret、private_key_jwt、mTLS | なし |
| PKCE | 推奨 | 必須(OAuth 2.1 基準) |
| token endpoint へのアクセス | シークレットとともに | code_verifier とともに |
OAuth 2.1 ドラフトはすべてのクライアントに PKCE を義務化し、Implicit/ROPC グラントを削除しました。2026 年時点では、新規クライアントはタイプを問わず PKCE(S256)を有効にするのが標準的なプラクティスです。Keycloak ではクライアントの Advanced 設定で Proof Key for Code Exchange Code Challenge Method を S256 に強制できます。
confidential client の認証方式も進化しています。単純な client secret よりも private_key_jwt(署名ベース)や mTLS が推奨され、26.6 で追加された Federated client authentication を使えば、SPIFFE/SVID のようなワークロードアイデンティティシステムが発行した JWT でクライアントを認証でき、シークレットそのものを排除できます。
Client Scope — トークンの中身を組み立てる単位
client scope は「トークンにどのクレームと role を入れるか」を再利用可能な単位にまとめたものです。protocol mapper(クレーム生成器)と role scope mapping(role フィルタ)の束だと考えると正確です。
- Default scope: 常にトークンに反映されます。profile、email、roles、web-origins がデフォルトです。
- Optional scope: クライアントが scope パラメータで明示的に要求したときだけ反映されます。address、phone、offline_access などが該当します。
例えば SPA が次のような認可リクエストを送ると:
GET /realms/production/protocol/openid-connect/auth?
client_id=web-app
&response_type=code
&scope=openid profile email offline_access
&redirect_uri=https://app.example.com/callback
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
openid、profile、email は default scope として自動的に含まれ、offline_access は optional scope のため明示的に要求してはじめて refresh token がオフライントークンとして発行されます。トークンの肥大化を防ぐには default scope を最小化し、audience mapper で aud クレームを明示的に管理するのがよいでしょう。発行された access token のペイロードはおおよそ次のようになります。
{
"exp": 1765540800,
"iat": 1765540500,
"iss": "https://sso.example.com/realms/production",
"aud": ["api-gateway"],
"sub": "f3a2c1d0-1234-5678-9abc-def012345678",
"typ": "Bearer",
"azp": "web-app",
"scope": "openid profile email",
"realm_access": { "roles": ["user"] },
"resource_access": {
"api-gateway": { "roles": ["orders:read"] }
},
"preferred_username": "alice"
}
認証フローエンジン — Browser Flow の内部構造
Keycloak の認証は authentication flow というツリー構造の実行エンジンとして動作します。browser flow が最も代表的で、ユーザーが認可エンドポイントに到着したときに実行されます。
Browser Flow (デフォルト構造)
|
+-- Cookie [ALTERNATIVE]
| (SSO セッションクッキーがあれば即座に通過)
|
+-- Identity Provider Redirector [ALTERNATIVE]
| (kc_idp_hint があれば外部 IdP へ)
|
+-- Forms [ALTERNATIVE]
|
+-- Username Password Form [REQUIRED]
|
+-- Browser - Conditional 2FA [CONDITIONAL]
|
+-- Condition - User Configured [REQUIRED]
+-- OTP Form [ALTERNATIVE]
+-- WebAuthn Authenticator [ALTERNATIVE]
各ノードは execution と呼ばれ、三つの要素で構成されます。
- Authenticator — 実際の認証ロジック(クッキー検査、パスワードフォーム、OTP 検証)を実行する SPI 実装
- Requirement — REQUIRED、ALTERNATIVE、CONDITIONAL、DISABLED のいずれか
- Sub-flow — execution をまとめるグループ(独自の requirement を持つ)
評価ルールはシンプルです。同じレベルで ALTERNATIVE のいずれかが成功すればそのレベルは通過、REQUIRED はすべて成功しなければ通過できません。CONDITIONAL sub-flow は内部の condition execution が真のときだけ REQUIRED のように動作します。上記の構造では「SSO クッキーがあればフォームをスキップ、なければパスワード入力後、OTP を設定したユーザーだけ 2FA」という動作がこのルールだけで実現されます。
26.x ではログインフォームに passkeys が統合され(conditional/modal UI)、WebAuthn conditional UI を通じてユーザーが ID 入力なしにブラウザのオートフィルから passkey を選択してログインするフローがデフォルトテーマでサポートされます。詳細は WebAuthn Level 3 仕様と FIDO Alliance の passkeys ドキュメントを参照してください。
認証が完了すると user session が作成され、クライアントごとの client session がその下にぶら下がります。これらのセッションは Infinispan キャッシュ(26 からはデフォルトで DB に永続化)に保存され、SSO クッキー(KEYCLOAK_IDENTITY)がブラウザに発行されます。
データベーススキーマの概要
Keycloak は JPA(Hibernate)ベースでリレーショナルデータベースにすべての設定とユーザーを保存します。中核となるテーブルグループを概観すると次のとおりです。
REALM -- realm 定義とポリシー (トークン有効期間など多数のカラム)
REALM_ATTRIBUTE -- realm の拡張属性 (key-value)
CLIENT -- クライアント定義
CLIENT_ATTRIBUTES -- クライアント拡張属性 (PKCE 強制など)
CLIENT_SCOPE -- client scope 定義
PROTOCOL_MAPPER -- クレームマッパー設定
USER_ENTITY -- ユーザー本体 (username, email, ...)
USER_ATTRIBUTE -- ユーザーカスタム属性
CREDENTIAL -- ハッシュ化パスワード、OTP、passkey メタデータ
USER_ROLE_MAPPING -- ユーザーと role のマッピング
KEYCLOAK_GROUP -- グループ (階層は PARENT_GROUP カラム)
GROUP_ROLE_MAPPING -- グループと role のマッピング
KEYCLOAK_ROLE -- realm/client role 定義
FEDERATED_IDENTITY -- 外部 IdP アカウントリンク
EVENT_ENTITY -- ログインイベント (有効化時)
ADMIN_EVENT_ENTITY -- 管理イベント監査ログ
OFFLINE_USER_SESSION -- オフライン/永続セッション
OFFLINE_CLIENT_SESSION -- クライアントごとの永続セッション
MIGRATION_MODEL -- スキーマバージョン追跡 (Liquibase)
運用時に知っておくと役立つポイントです。
- スキーマのマイグレーションは Liquibase が起動時に自動実行します。大規模アップグレードの前には必ず DB バックアップが必要です。
- EVENT_ENTITY は保存期間を設定しないと無限に成長します。イベントの有効期限設定または外部ログシステムとの連携が必須です。
- 属性検索が頻繁なら USER_ATTRIBUTE へのインデックス追加を検討します(26 は主要な参照経路のインデックスを強化しました)。
- 直接 SQL でデータを変更するのはキャッシュ不整合を引き起こすため禁忌です。必ず Admin API を使用します。
kc.sh — Build と Start の二段階構造
Quarkus ベース Keycloak の最大の特徴は、設定がビルドオプションとランタイムオプションに分かれている点です。
- ビルドオプション: DB の種類、キャッシュスタック、health/metrics の有効化、features フラグなど — 変更時は再ビルドが必要
- ランタイムオプション: DB の URL/アカウント、hostname、ログレベル、HTTPS 証明書パスなど — 起動時に指定
# 第 1 段階: ビルド (イミュータブルイメージを作るときに一度だけ)
bin/kc.sh build \
--db=postgres \
--health-enabled=true \
--metrics-enabled=true \
--features=token-exchange
# 第 2 段階: 起動 (ランタイムオプションのみ指定、--optimized で再ビルドを省略)
bin/kc.sh start --optimized \
--db-url=jdbc:postgresql://db.internal:5432/keycloak \
--db-username=keycloak \
--db-password=changeme \
--hostname=sso.example.com \
--proxy-headers=xforwarded \
--http-enabled=true
コンテナイメージを作る際は、ビルド段階を Dockerfile で事前に実行して起動時間を短縮するのが定石です。
FROM quay.io/keycloak/keycloak:26.6 AS builder
ENV KC_DB=postgres
ENV KC_HEALTH_ENABLED=true
ENV KC_METRICS_ENABLED=true
RUN /opt/keycloak/bin/kc.sh build
FROM quay.io/keycloak/keycloak:26.6
COPY /opt/keycloak/ /opt/keycloak/
ENTRYPOINT ["/opt/keycloak/bin/kc.sh", "start", "--optimized"]
start-dev モードは開発専用です。キャッシュがローカルで HTTP が開いており hostname 検証が緩いため、プロダクションでは絶対に使用してはいけません。
設定方法 — 環境変数、conf ファイル、CLI
同じオプションを三つの方法で指定でき、優先順位は CLI 引数が最も高く、次に環境変数、conf ファイルの順です。
# 方法 1: CLI 引数
bin/kc.sh start --db-url-host=db.internal --log-level=info
# 方法 2: 環境変数 (オプション名を大文字+アンダースコアに、KC_ プレフィックス)
export KC_DB_URL_HOST=db.internal
export KC_LOG_LEVEL=info
# 方法 3: conf/keycloak.conf ファイル
# db-url-host=db.internal
# log-level=info
Kubernetes 環境では環境変数方式が ConfigMap/Secret と自然に組み合わさるため最も広く使われています。シークレット値をファイルとしてマウントし、オプション値にファイル参照を使う方式もサポートされています。全オプション一覧は kc.sh のサブコマンドごとの help と公式サーバー設定ガイドで確認できます。
Keycloak 26.x の新機能まとめ
26.x シリーズ、とりわけ 26.6.0 リリースには大きな機能が多数含まれています。
Workflows (realm 管理の自動化)
長期間未使用のアカウントの無効化、一定期間後の権限剥奪といったライフサイクル作業をサーバー内で宣言的に自動化する機能です。従来は外部バッチジョブから Admin API を呼び出して処理していた作業を、realm 設定として内在化できます。
Zero-downtime rolling patch
26.6 からパッチバージョン間(26.6.0 から 26.6.1 など)ではキャッシュ互換性が保証され、Kubernetes の rolling update でクラスタ全体を再起動せずに Pod を順次入れ替えられます。Operator 使用時は update strategy を Auto にすると互換性チェック後に自動でローリングが実行されます。
Federated client authentication
クライアントが client secret の代わりに外部の信頼機関(例: SPIFFE ワークロードアイデンティティ)が発行した JWT で認証します。シークレットの配布/ローテーション問題を根本的に排除する方向性です。
JWT Authorization Grant
RFC 7523 ベースで、外部で発行された JWT assertion を access token に交換するグラントが正式サポートされました。システム間連携(machine-to-machine)でのトークンブローカリングがシンプルになります。
その他
- ログインフォームへの passkeys 統合(conditional/modal UI)
- FAPI 2.0 Security Profile & Message Signing Final のサポート
- EdDSA 署名アルゴリズムのサポート
- OAuth Client ID Metadata Document(CIMD)の実験的サポート — Keycloak を MCP(Model Context Protocol)の authorization server として使うシナリオが開かれました。AI エージェントが事前登録なしにメタデータ URL で自身を識別するパターンです。
プロダクションチェックリスト
最後に、プロダクション投入前の点検リストです。
[ ] master realm にアプリケーションユーザーを置いていないか
[ ] admin コンソールへのアクセスを別の hostname またはネットワークに制限したか
(--hostname-admin オプション、ファイアウォール/Ingress の分離)
[ ] HTTPS 終端と --proxy-headers の設定が一致しているか
[ ] DB はプロダクション級 (PostgreSQL 推奨) でコネクションプールサイズを見積もったか
[ ] トークン有効期間: access token 5-15 分、SSO idle/max ポリシーを策定したか
[ ] refresh token rotation または revoke-refresh-token を有効にしたか
[ ] すべての新規クライアントに PKCE S256 を強制したか
[ ] ワイルドカード redirect URI を禁止したか (正確な URI のみ登録)
[ ] brute force detection を有効にしたか
[ ] イベントロギング + 保存期間 + 外部 SIEM 連携を構成したか
[ ] health/metrics エンドポイントを有効にしモニタリングに接続したか
[ ] バックアップ: DB バックアップ + realm export を定期的に実行しているか
[ ] アップグレード経路: リリースノートの breaking change を追跡しているか
[ ] features フラグで使わない機能 (例: 一時 admin アカウント) を整理したか
特にトークンポリシーは OAuth 2.0 Security BCP(RFC 9700)を基準に定めることを推奨します。sender-constrained token(DPoP/mTLS)、短い access token 有効期間、refresh token rotation が核心です。
トラブルシューティング — よくある問題と原因
1. Invalid redirect_uri エラー
最もよくある初期設定エラーです。クライアントに登録された redirect URI と認可リクエストの redirect_uri が正確に一致しないときに発生します。
原因チェックリスト:
- プロトコルの不一致 (http vs https)
- 末尾スラッシュの有無の違い
- ポートの欠落 (https://app.example.com vs https://app.example.com:443)
- ワイルドカード(*)に依存していてプロダクションで削除されたケース
解決はシンプルです。クライアント設定の Valid redirect URIs にコールバック URL を正確に(可能ならワイルドカードなしで)登録します。
2. リバースプロキシの背後で無限リダイレクトまたは誤った issuer
プロキシが TLS を終端しているのに Keycloak がそれを知らないと、発行されるトークンの iss クレームとリダイレクト URL が内部アドレスで生成されます。
# プロキシが X-Forwarded-* ヘッダーを設定している場合
bin/kc.sh start --optimized \
--hostname=sso.example.com \
--proxy-headers=xforwarded \
--http-enabled=true
# 検証: OIDC discovery ドキュメントの issuer が外部アドレスかを確認
curl -s https://sso.example.com/realms/production/.well-known/openid-configuration
discovery ドキュメントの issuer、authorization_endpoint がいずれも外部 hostname になっているかを確認すればよいでしょう。
3. ビルドオプションをランタイムで変えたのに反映されない
DB の種類や features のようなビルドオプションは、start 時点で指定しても無視されるか自動再ビルドを誘発します。--optimized フラグを使っていれば無視され、そうでなければ起動が遅くなります。ビルドオプションの変更は必ずイメージの再ビルドで処理しなければなりません。
4. アップグレード後にログインセッションがすべてログアウト
26 以前のバージョンではインメモリセッションがデフォルトだったため、全体再起動でセッションが消失していました。26 から persistent user sessions がデフォルトになりセッションは DB に保存されますが、マイナーバージョン間のキャッシュプロトコル非互換によるフル再起動は依然として必要になる場合があります。リリースノートのアップグレードセクションを必ず確認してください。
5. メモリ使用量が増え続ける
よくある原因は次の三つです。
1. EVENT_ENTITY / ADMIN_EVENT_ENTITY の無限増加
-> イベント有効期限(expiration)の設定、外部 SIEM へのオフロード
2. オフラインセッション過多 (offline_access を全クライアントに付与)
-> offline_access scope を本当に必要なクライアントに限定
3. JVM ヒープ未設定でコンテナの上限までキャッシュが成長
-> JAVA_OPTS_KC_HEAP またはコンテナメモリベースの自動算定を確認
アンチパターン集
- master realm を一般ユーザーのログインに使用 — 権限昇格リスクと管理の複雑さを同時に増大させます。
- ROPC(Resource Owner Password Credentials)グラントの使用 — OAuth 2.1 で削除されたパターンです。CLI なら Device Authorization Grant を使用します。
- access token の有効期間を数時間に設定 — 漏洩時の被害範囲がそのまま大きくなります。5-15 分 + refresh rotation が標準です。
- DB を直接 UPDATE — キャッシュと DB の不整合により予測不能な動作を引き起こします。Admin REST API または Admin CLI(kcadm.sh)だけを使用します。
- すべての role を composite role で連結 — 権限監査が不可能になります。group ベースのマッピングで解きほぐす方が良いです。
おわりに
Keycloak 26 は「WildFly 時代の重い SSO サーバー」という印象とはまったく異なるソフトウェアになりました。Quarkus ベースのビルド/ランタイム分離、宣言的設定、Kubernetes フレンドリーな運用モデル、そして passkeys と AI エージェントアイデンティティまで包括する標準サポートは、2026 年の identity-first な Zero Trust アーキテクチャにおいて Keycloak を中心コンポーネントにするのに十分です。
本記事で扱ったドメインモデルとランタイム構造を理解できたなら、次のステップは高可用性クラスタリングと SPI 拡張開発です。それぞれ別の記事で深く扱います。