1. 概要

Keycloakは、OpenID Connect(OAuth 2.0 への拡張)とSAML 2.0の両方をサポートしています。クライアントとサービスをセキュリティー保護する際に最初に決定すべきことは、どちらを使用するのかということです。必要に応じて、一部をOpenID Connectで、それ以外をSAMLでセキュリティー保護することも選択できます。

クライアントとサービスをセキュリティー保護するためには、選択したプロトコルに対応するアダプターやライブラリーも必要になります。Keycloakには選択したプラットフォームに対応する独自のアダプターが付属していますが、一般的なOpenID Connectのリライング・パーティーやSAMLのサービス・プロバイダーのライブラリーを使用することもできます。

1.1. クライアント・アダプターとは?

Keycloakのクライアント・アダプターは、Keycloakとともに使用してアプリケーションとサービスを非常に簡単にセキュリティー保護できるライブラリーです。基盤となるプラットフォームやフレームワークとのタイトな統合を提供するので、ライブラリーではなく、アダプターと呼んでいます。このような考え方により、アダプターは使いやすく、一般的にライブラリーが必要とするよりも定型コードが少なくて済みます。

1.2. サポートされているプラットフォーム

1.2.1. OpenID Connect

JavaScript(クライアントサイド)
Node.js(サーバーサイド)
C#
Python
Android
iOS
Apache HTTP Server

1.2.2. SAML

Apache HTTP Server

1.3. サポートされているプロトコル

1.3.1. OpenID Connect

OpenID Connect (OIDC)は OAuth 2.0 を拡張した認証プロトコルです。OAuth 2.0が認可プロトコルのみを構築するためのフレームワークで、不完全であるのに対し、OIDCは完成された認証および認可のプロトコルです。OIDCはまた、 Json Web Token (JWT)の標準セットを多用します。これらの標準は、コンパクトでWebフレンドリーな方法で、アイデンティティー・トークンのJSON形式とデジタル署名、データ暗号化の方法を定義します。

OIDCを使用するユースケースは、実際には2つの種類があります。1つ目は、Keycloakサーバーにユーザーの認証を要求するアプリケーションのユースケースです。ログインが成功すると、アプリケーションは IDトークンアクセストークン を受け取ります。 IDトークン には、ユーザーに関する情報(ユーザー名、電子メール、その他のプロファイル情報など)が含まれています。 アクセストークン はレルムによってデジタル署名されており、アプリケーションがユーザーのアクセス可能なリソースを決定するために使用できるアクセス情報(ユーザー・ロール・マッピングのような)が含まれています。

2つ目のタイプは、リモートサービスへのアクセス権を取得したいクライアントのユースケースです。この場合、クライアントは、ユーザーの代理として他のリモートサービスの呼び出しに使用できる アクセストークン の取得をKeycloakに要求します。Keycloakは、ユーザーを認証し、クライアントのアクセスを許可する同意を要求します。そして、クライアントは アクセストークン を受け取ります。この アクセストークン はレルムによってデジタル署名されます。クライアントはこの アクセストークン を使用して、リモートサービスのREST呼び出しを行うことができます。REST サービスは アクセストークン を抽出し、トークンの署名を検証し、トークン内のアクセス情報に基づいて、リクエストを処理するかどうかを決定します。

1.3.2. SAML 2.0

SAML 2.0は、OIDCに似ていますが、それよりも古く成熟した仕様です。SOAPと過多なWS-*の仕様をそのルーツを持っているため、OIDCよりも少し冗長になる傾向があります。SAML 2.0は、主に認証サーバーとアプリケーション間のXMLドキュメントの交換によって動作する認証プロトコルです。XML署名と暗号化を使用して、リクエストとレスポンスを検証します。

Keycloakでは、SAMLはブラウザー・アプリケーションとREST呼び出しの2種類のユースケースを提供しています 。

SAMLを使用するユースケースは、実際には2つの種類があります。1つ目は、Keycloakサーバーにユーザーの認証を要求するアプリケーションのユースケースです。ログインが成功すると、アプリケーションはユーザーに関する様々な属性を指定するSAMLアサーションと呼ばれるものが含まれるXMLドキュメントを受け取ります。このXMLドキュメントはレルムによってデジタル署名されており、アプリケーションがユーザーのアクセス可能なリソースを決定するために使用できるアクセス情報(ユーザー・ロール・マッピングなど)が含まれています。

2つ目のタイプは、リモートサービスへのアクセス権を取得したいクライアントのユースケースです。この場合、クライアントは、ユーザーの代理として他のリモートサービスの呼び出しに使用できるSAMLアサーションの取得をKeycloakに要求します。

1.3.3. OpenID Connect vs SAML

OpenID ConnectとSAMLの選択は、古いより成熟したプロトコル(SAML)の代わりに新しいプロトコル(OIDC)を使用するだけの問題ではありません。

ほとんどの場合において、KeycloakではOIDCを使用することをお勧めします。

SAMLは、OIDCよりも少し冗長になる傾向があります。

交換するデータの詳細度を度外視して仕様を比較した場合、OIDCはもともとWebで動作するように設計されていますが、SAMLはWeb上で動作するように改造されていることが分かります。たとえば、OIDCはSAMLよりもクライアントサイドに実装することが簡単なため、HTML5/JavaScriptアプリケーションにも適しています。トークンはJSON形式なので、JavaScriptにより簡単に扱うことができます。また、Webアプリケーションに対してセキュリティーの実装を容易にする、いくつかの素晴らしい機能があります。たとえば、ユーザーがログインしているかどうかを容易に判断するために使用する iframeトリック の仕様をチェックしてください。

SAMLにも用途はあります。OIDCの仕様の進化を見ると、SAMLが長年に渡って実装してきた多数の機能がOIDCにも実装されていることが分かります。SAMLがOIDCより成熟しているという認識と、SAMLによりセキュリティー保護されている既存のアプリケーションが存在するという理由により、OIDCよりもSAMLが選ばれることは多々あります。

2. OpenID Connect

このセクションでは、Keycloakアダプターまたは汎用OpenID Connectリライング・パーティーのライブラリーを使用して、アプリケーションとサービスをOpenID Connectでセキュリティー保護する方法について説明します。

2.1. Javaアダプター

Keycloakには、Javaアプリケーション用のさまざまなアダプターが付属しています。正しいアダプターの選択は、対象のプラットフォームによって異なります。

全てのJavaアダプターは、Javaアダプターの設定の章で説明されている共通の設定オプションのセットを共有しています。

2.1.1. Javaアダプターの設定

Keycloakでサポートされている各Javaアダプターは、単純なJSONファイルで設定できます。これは、次のようになります。

{
  "realm" : "demo",
  "resource" : "customer-portal",
  "realm-public-key" : "MIGfMA0GCSqGSIb3D...31LwIDAQAB",
  "auth-server-url" : "https://localhost:8443/auth",
  "ssl-required" : "external",
  "use-resource-role-mappings" : false,
  "enable-cors" : true,
  "cors-max-age" : 1000,
  "cors-allowed-methods" : "POST, PUT, DELETE, GET",
  "cors-exposed-headers" : "WWW-Authenticate, My-custom-exposed-Header",
  "bearer-only" : false,
  "enable-basic-auth" : false,
  "expose-token" : true,
  "verify-token-audience" : true,
   "credentials" : {
      "secret" : "234234-234234-234234"
   },

   "connection-pool-size" : 20,
   "disable-trust-manager": false,
   "allow-any-hostname" : false,
   "truststore" : "path/to/truststore.jks",
   "truststore-password" : "geheim",
   "client-keystore" : "path/to/client-keystore.jks",
   "client-keystore-password" : "geheim",
   "client-key-password" : "geheim",
   "token-minimum-time-to-live" : 10,
   "min-time-between-jwks-requests" : 10,
   "public-key-cache-ttl": 86400,
   "redirect-rewrite-rules" : {
   "^/wsmaster/api/(.*)$" : "/api/$1"
   }
}

${…​} エンクロージャーを使用して、システム・プロパティーの置換を行うことができます。たとえば、 ${jboss.server.config.dir}/path/to/Keycloak に置換できます。環境変数の置換では、 env プレフィックスもサポートされています。たとえば、 ${env.MY_ENVIRONMENT_VARIABLE} です。

初期設定ファイルは、管理コンソールから取得できます。これを行うには、管理コンソールを開き、メニューから Clients を選択し、対応するクライアントをクリックします。クライアントのページが開かれたら、 Installation タブをクリックし、 Keycloak OIDC JSON を選択します。

各設定オプションの説明は次のとおりです。

realm

レルムの名前。 これは REQUIRED です。

resource

アプリケーションのクライアントID。各アプリケーションには、アプリケーションを識別するために使用されるクライアントIDがあります。これは REQUIRED です。

realm-public-key

レルム公開鍵のPEM形式です。これは管理コンソールから取得できます。これは OPTIONAL であり、設定することはお勧めしません。これが設定されていない場合、アダプターは必要に応じて(Keycloakの鍵をローテーションさせる場合など)、Keycloakから常に再ダウンロードします。しかし、realm-public-keyが設定されている場合、アダプターはKeycloakから新しい鍵をダウンロードすることは無いので、Keycloakの鍵をローテーションするとアダプターが正常に動作しなくなります。

auth-server-url

KeycloakサーバーのベースURLです。すべてのKeycloakページとRESTサービスのエンドポイントは、これから派生しています。通常、 https://host:port/auth の形式です。これは REQUIRED です。

ssl-required

Keycloakサーバーとの間のすべての通信がHTTPS経由であることを保証します。プロダクション環境では、これを all に設定する必要があります。これは OPTIONAL です。デフォルト値は external です。つまり、外部リクエストに対してデフォルトでHTTPSが必要です。有効な値は allexternalnone です。

confidential-port

SSL/TLSを介した安全な接続のために、Keycloakサーバーが使用する機密ポートです。これは OPTIONAL です。デフォルト値は 8443 です。

use-resource-role-mappings

trueに設定すると、アダプターは、ユーザーのアプリケーション・レベルのロール・マッピングのトークンを調べます。falseの場合、ユーザー・ロール・マッピングのレルム・レベルを確認します。これは OPTIONAL です。デフォルト値は false です。

public-client

trueに設定すると、アダプターはクライアントのクレデンシャルをKeycloakに送信しません。これは OPTIONAL です。デフォルト値は false です。

enable-cors

これにより、CORSサポートが有効になります。CORSプリフライト・リクエストを処理します。また、アクセス・トークンを調べて、有効なオリジンを特定します。これは OPTIONAL です。デフォルト値は false です。

cors-max-age

CORSが有効な場合、これは Access-Control-Max-Age ヘッダーの値を設定します。これは OPTIONAL です。設定されていない場合、このヘッダーはCORSレスポンスで返されません。

cors-allowed-methods

CORSが有効な場合、これは Access-Control-Allow-Methods ヘッダーの値を設定します。 これはカンマ区切りの文字列でなければなりません。これは OPTIONAL です。設定されていない場合、このヘッダーはCORSレスポンスで返されません。

cors-allowed-headers

CORSが有効な場合、これは Access-Control-Allow-Headers ヘッダーの値を設定します。 これはカンマ区切りの文字列でなければなりません。これは OPTIONAL です。設定されていない場合、このヘッダーはCORSレスポンスで返されません。

cors-exposed-headers

CORSが有効な場合、これは Access-Control-Expose-Headers ヘッダーの値を設定します。 これはカンマ区切りの文字列でなければなりません。これは OPTIONAL です。設定されていない場合、このヘッダーはCORSレスポンスで返されません。

bearer-only

これは、サービスに対して true に設定する必要があります。有効にすると、アダプターはユーザーを認証しようとせず、ベアラー・トークンのみを検証します。これは OPTIONAL です。デフォルト値は false です。

autodetect-bearer-only

アプリケーションがWebアプリケーションとWebサービス(SOAPやRESTなど)の両方を提供する場合は、これを true に設定する必要があります。 Webアプリケーションの未認証のユーザーをKeycloakのログインページにリダイレクトできますが、ログインページへのリダイレクトを理解できない未認証のSOAPクライアントまたはRESTクライアントにはHTTPの 401 ステータスコードを送信します。Keycloakは、 X-Requested-WithSOAPActionAccept のような典型的なヘッダーに基づいて、SOAPクライアントまたはRESTクライアントを自動検出します。デフォルト値は false です。

enable-basic-auth

これは、BASIC認証もサポートするようにアダプターに指示します。このオプションを有効にすると、 secret も指定する必要があります。これは OPTIONAL です。 デフォルト値は false です。

expose-token

true の場合、(JavaScriptのHTTP呼び出しを介して)認証されたブラウザー・クライアントが、 root/k_query_bearer_token のURLを介して署名付きのアクセストークンを取得できます。これは OPTIONAL です。デフォルト値は false です。

credentials

アプリケーションのクレデンシャルを指定します。これは、キーがクレデンシャル・タイプで、値がクレデンシャル・タイプの値であるオブジェクト表記法です。 現在、パスワードとJWTがサポートされています。これは、 Confidential のアクセス・タイプのクライアントの場合にのみ REQUIRED です。

connection-pool-size

この設定オプションは、Keycloakサーバーにプールすべき接続数を定義します。これは OPTIONAL です。デフォルトは 20 です。

disable-trust-manager

KeycloakサーバーがHTTPSを必要とし、この設定オプションを true に設定する場合は、トラストストアを指定する必要はありません。この設定は、SSL証明書の検証を無効とするため、開発時にのみ使用すべきで、プロダクション環境では 決して使用してはいけません 。これは OPTIONAL です。デフォルトは false です。

allow-any-hostname

KeycloakサーバーがHTTPSを必要とし、この設定オプションを true に設定する場合、トラストストア経由でKeycloakサーバーの証明書は検証されますが、ホスト名の検証は行われません。SSL証明書の検証が無効となるため、この設定は開発時にのみ使用すべきで、プロダクション環境では 決して使用してはいけません 。これは OPTIONAL です。デフォルトは false です。

proxy-url

HTTPプロキシーを使用する場合はそのURLを設定します。

truststore

トラストストア・ファイルへのファイルパスです。パスに classpath: を付けると、トラストストアはデプロイメントのクラスパスから取得されます。これはKeycloakサーバーへのHTTPS通信に使用されます。HTTPSリクエストを行うクライアントは、通信を行うサーバーのホストを検証する必要があります。これがトラストストアの役割です。キーストアには、1つ以上の信頼されたホストの証明書または認証局が含まれています。このトラストストアは、KeycloakサーバーのSSLキーストアのパブリック証明書を抽出することで作成できます。これは、 ssl-requirednonedisable-trust-managertrue でない限り、 REQUIRED です。

truststore-password

トラストストアのパスワードです。これは REQUIRED で、 truststore を設定すると、トラストストアはパスワードを必要とします。

client-keystore

キーストア・ファイルへのファイルパスです。このキーストアには、アダプターがKeycloakサーバーにHTTPSリクエストを行う際の、双方向SSLのためのクライアント証明書を含めます。これは OPTIONAL です。

client-keystore-password

クライアント・キーストア用のパスワードです。 client-keystore が設定されている場合、これは REQUIRED です。

client-key-password

クライアントキー用のパスワードです。 client-keystore が設定されている場合、これは REQUIRED です。

always-refresh-token

true の場合、アダプターはすべてのリクエストでトークンをリフレッシュします。警告 - 有効にすると、アプリケーションへのすべてのリクエストに対してKeycloakへのリクエストが発生します。

register-node-at-startup

true の場合、アダプターはKeycloakに登録リクエストを送信します。これはデフォルトで false であり、アプリケーションがクラスター化されている場合にのみ有効です。詳細はアプリケーション・クラスタリングを参照してください。

register-node-period

Keycloakへアダプターを再登録する期間です。アプリケーションがクラスター化されている場合に便利です。詳細はアプリケーション・クラスタリングを参照してください。

token-store

設定可能な値は sessioncookie です。デフォルトは session です。つまり、アダプターがアカウント情報をHTTPセッションに保管します。一方、 cookie は情報をクッキーに格納することを意味します。詳細はアプリケーション・クラスタリングを参照してください。

token-cookie-path

Cookieストアを使用する場合、このオプションにはアカウント情報を保存するために使用されるCookieのパスを設定します。相対パスの場合、アプリケーションはコンテキスト・ルートで実行されていると見なされ、そのコンテキスト・ルートに対して相対的に解釈されます。絶対パスの場合は、絶対パスを使用してCookieのパスを設定します。デフォルトではコンテキスト・ルートからの相対パスを使用します。

principal-attribute

ユーザー・プリンシパル名とするOpenID ConnectのIDトークン属性を設定します。トークン属性がnullの場合、デフォルトは sub になります。有効な値は subpreferred_usernameemailnamenicknamegiven_namefamily_name です。

turn-off-change-session-id-on-login

一部のプラットフォームでは、セキュリティー攻撃口とならないように、正常にログインするとデフォルトでセッションIDが変更されます。これを無効にするには true に変更します。これは OPTIONAL です。デフォルト値は false です。

token-minimum-time-to-live

アクセストークンの有効期限が切れる前に、Keycloakサーバーでアクティブなアクセストークンを先にリフレッシュする時間です(秒単位)。これは、アクセストークンが評価される前に期限切れになる可能性がある別のRESTクライアントに送信されるときに特に便利です。この値は、レルムのアクセストークンの寿命を超えてはなりません。これは OPTIONAL です。デフォルト値は 0 秒なので、アダプターは期限切れになったときだけ、アクセストークンを更新します。

min-time-between-jwks-requests

新しい公開鍵を取得するための、Keycloakへの2つのリクエスト間の最小間隔を指定する時間です(秒単位)。デフォルトでは10秒です。アダプターは、未知の kid を含むトークンを認識すると、常に新しい公開鍵をダウンロードしようとします。ただし、(デフォルトでは)10秒に1回以上試行しません。これは、攻撃者が不正な kid で大量のトークンを送りつけて、アダプターからKeycloakに大量のリクエストを送信させるというDoSを防ぐために使用されます。

public-key-cache-ttl

新しい公開鍵を取得するための、Keycloakへの2つのリクエスト間の最大間隔を指定する時間です(秒単位)。デフォルトでは86400秒(1日)です。アダプターは、未知の kid を含むトークンを認識すると、常に新しい公開鍵をダウンロードしようとします。既知の kid のトークンを認識すれば、以前にダウンロードした公開鍵だけを使用します。しかし、この設定された間隔(デフォルトでは1日)に少なくとも1回は、トークンの kid が既知でも常に新しい公開鍵がダウンロードされます。

ignore-oauth-query-parameter

デフォルトは false です。 true に設定されていると、ベアラー・トークン処理のための access_token クエリー・パラメーターの処理が無効になります。ユーザーは、 access_token を渡すだけで認証することはできません。

redirect-rewrite-rules

必要に応じて、リダイレクトURIリライトのルールを指定します。キーがリダイレクトURIに一致する正規表現であり、値が置換文字列であるオブジェクト表記法で表します。置換文字列の後方参照に $ 文字を使用できます。

verify-token-audience

true に設定されている場合、ベアラートークンによる認証中に、アダプターはトークンにこのクライアント名(リソース)がAudienceとして含まれているかどうかを検証します。このオプションは、ベアラートークンによって認証されたリクエストを主に処理するサービスに特に便利です。これはデフォルトで false に設定されていますが、セキュリティーを強化するため、これを有効にすることをお勧めします。オーディエンス・サポートの詳細については、 Audienceのサポート を参照してください。

2.1.2. JBoss EAP/WildFlyアダプター

JBoss EAP、WildFly、またはJBoss ASにデプロイされたWARアプリケーションをセキュリティー保護するには、Keycloakアダプター・サブシステムをインストールして設定する必要があります。さらに、WARをセキュリティー保護する2つのオプションがあります。

WARにアダプター設定ファイルを用意し、web.xml内のauth-methodをKEYCLOAKに変更することができます。

あるいは、WARをまったく変更せずに、 standalone.xml などの設定ファイルのKeycloakアダプター・サブシステム設定で保護することもできます。このセクションで、両方の方法について説明します。

アダプターのインストール

アダプターは、使用しているサーバーのバージョンに応じて別個のアーカイブとして利用できます。

今回のリリースでは、WildFlyの最新バージョンでのみアダプターのテストとメンテナンスを行っています。WildFlyの新しいバージョンがリリースされると、現在のアダプターは非推奨になり、WildFlyの次のリリース後にサポートが削除されます。もう一つの選択肢は、JBoss EAPアダプターの場合はより長期間サポートされるので、アプリケーションをWildFlyからJBoss EAPに切り替えることです。

WildFly 9以上へのインストールは次のとおりです。

$ cd $WILDFLY_HOME
$ unzip keycloak-wildfly-adapter-dist-11.0.0.zip

JBoss EAP 7へのインストールは次のとおりです。

$ cd $EAP_HOME
$ unzip keycloak-eap7-adapter-dist-11.0.0.zip

JBoss EAP 6へのインストールは次のとおりです。

$ cd $EAP_HOME
$ unzip keycloak-eap6-adapter-dist-11.0.0.zip

JBoss AS 7.1へのインストールは次のとおりです。

$ cd $JBOSS_HOME
$ unzip keycloak-as7-adapter-dist-11.0.0.zip

このZIPアーカイブには、Keycloakアダプター固有のJBossモジュールが含まれています。また、アダプター・サブシステムを設定するためのJBoss CLIスクリプトも含まれています。

サーバーが起動していない場合に、アダプター・サブシステムを設定するには、次のようにします。

あるいは、別の設定を使用してアダプターをインストールするため、コマンドラインからアダプターをインストールする際に server.config プロパティーを指定することもできます。たとえば、 -Dserver.config=standalone-ha.xml とします。
WildFly 11以上
$ ./bin/jboss-cli.sh --file=bin/adapter-elytron-install-offline.cli
WildFly 10以前
$ ./bin/jboss-cli.sh --file=bin/adapter-install-offline.cli
WildFly 11以上でもレガシーな非Elytronアダプターが使用できます。つまり、それらのバージョンでも adapter-install-offline.cli を使用することができます。ただし、新しいElytronアダプターを使用することをお勧めします。

あるいは、サーバーが起動している場合は、次のように実行します。

WildFly 11以上
$ ./bin/jboss-cli.sh -c --file=bin/adapter-elytron-install.cli
WildFly 10以前
$ ./bin/jboss-cli.sh -c --file=bin/adapter-install.cli
JBoss SSO

WildFlyには、同じWildFlyインスタンスにデプロイされたWebアプリケーションのシングル・サインオンのサポートが組み込まれています。これは、Keycloakを使用しているときは有効にしないでください。

WARごとに必要な設定

このセクションでは、直接WARパッケージ内に設定を追加し、ファイルを編集することで、WARをセキュリティー保護する方法について説明します。

まず、WARの WEB-INF ディレクトリーに keycloak.json アダプター設定ファイルを作成しておく必要があります。

この設定ファイルの形式は、Javaアダプターの設定のセクションで説明しています。

次に、 web.xml 内の auth-methodKEYCLOAK に設定する必要があります。また、標準のサーブレット・セキュリティーを使用して、URLのロールベース制約を指定する必要があります。

以下は例です。

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      version="3.0">

    <module-name>application</module-name>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Admins</web-resource-name>
            <url-pattern>/admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>admin</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Customers</web-resource-name>
            <url-pattern>/customers/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>user</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>

    <login-config>
        <auth-method>KEYCLOAK</auth-method>
        <realm-name>this is ignored currently</realm-name>
    </login-config>

    <security-role>
        <role-name>admin</role-name>
    </security-role>
    <security-role>
        <role-name>user</role-name>
    </security-role>
</web-app>
アダプター・サブシステムによるWARの保護

KeycloakでWARを修正する必要はありません。代わりに、Keycloakアダプター・サブシステムを介して外部から保護することができます。 auth-methodKEYCLOAK を指定する必要はありませんが、 web.xmlsecurity-constraints を定義する必要はあります。しかし、 WEB-INF/keycloak.json ファイルを作成する必要はありません。このメタデータは代わりに、Keycloakサブシステム定義のサーバー設定(つまり、 standalone.xml )内で定義されます。

<extensions>
  <extension module="org.keycloak.keycloak-adapter-subsystem"/>
</extensions>

<profile>
  <subsystem xmlns="urn:jboss:domain:keycloak:1.1">
     <secure-deployment name="WAR MODULE NAME.war">
        <realm>demo</realm>
        <auth-server-url>http://localhost:8081/auth</auth-server-url>
        <ssl-required>external</ssl-required>
        <resource>customer-portal</resource>
        <credential name="secret">password</credential>
     </secure-deployment>
  </subsystem>
</profile>

secure-deploymentname 属性は保護したいWARを識別します。 web.xml 内の module-name.war を付加した値となります。残りの設定は、Java アダプターの設定で定義されている keycloak.json 設定オプションと一対一で対応しています。

例外は credential 要素です。

より簡単に行うには、Keycloak管理コンソールにアクセスし、このWARが並んでいるアプリケーションのClient/Installationタブに移動します。そこで、カット・アンド・ペーストできるサンプルXMLファイルが提供されてます。

同じレルムで複数のデプロイメントをセキュリティー保護している場合は、別の要素でレルム設定を共有できます。以下に例を示します。

<subsystem xmlns="urn:jboss:domain:keycloak:1.1">
    <realm name="demo">
        <auth-server-url>http://localhost:8080/auth</auth-server-url>
        <ssl-required>external</ssl-required>
    </realm>
    <secure-deployment name="customer-portal.war">
        <realm>demo</realm>
        <resource>customer-portal</resource>
        <credential name="secret">password</credential>
    </secure-deployment>
    <secure-deployment name="product-portal.war">
        <realm>demo</realm>
        <resource>product-portal</resource>
        <credential name="secret">password</credential>
    </secure-deployment>
    <secure-deployment name="database.war">
        <realm>demo</realm>
        <resource>database-service</resource>
        <bearer-only>true</bearer-only>
    </secure-deployment>
</subsystem>
セキュリティー・ドメイン

セキュリティー・コンテキストは、EJB層に自動的に伝播されます。

2.1.3. RPMからのJBoss EAPアダプターのインストール

次のように、RPMからEAP 7アダプターをインストールします。

Red Hat Enterprise Linux 7では、チャンネルという用語はリポジトリーという用語に置き換えられました。これらの説明では、リポジトリーという用語のみが使用されています。

RPMからWildFly 7アダプターをインストールする前に、WildFly 21リポジトリーをサブスクライブする必要があります。

前提条件
  1. Red Hat Subscription Managerを使用して、Red Hat Enterprise Linuxシステムがアカウントに登録されていることを確認してください。詳細は、Red Hat Subscription Management documentationのリンクを参照してください。

  2. すでに別のJBoss EAPリポジトリーに登録している場合は、まずそのリポジトリーから登録を解除する必要があります。

Red Hat Enterprise Linux 6、7の場合:Red Hat Subscription Managerを使用して、次のコマンドでWildFly 21リポジトリーに登録します。Red Hat Enterprise Linuxバージョンに応じて、 <RHEL_VERSION> を6または7のいずれかに置き換えてください。

$ sudo subscription-manager repos --enable=jb-eap-7-for-rhel-<RHEL_VERSION>-server-rpms

Red Hat Enterprise Linux 8の場合:Red Hat Subscription Managerを使用して、次のコマンドでWildFly 21リポジトリーに登録します。

$ sudo subscription-manager repos --enable=jb-eap-21-for-rhel-8-x86_64-rpms --enable=rhel-8-for-x86_64-baseos-rpms --enable=rhel-8-for-x86_64-appstream-rpms

Red Hat Enterprise Linux 6、7で次のコマンドを使用して、OIDC用のWildFly 7アダプターをインストールします。

$ sudo yum install eap7-keycloak-adapter-sso7_4

または、次のいずれかをRed Hat Enterprise Linux 8に使用します。

$ sudo dnf install eap7-keycloak-adapter-sso7_4
RPMインストールのためのデフォルトのEAP_HOMEパスは、/opt/rh/eap7/root/usr/share/wildflyです。

適切なモジュール・インストール・スクリプトを実行します。

OIDCモジュールの場合は、次のコマンドを実行します。

$ $EAP_HOME/bin/jboss-cli.sh -c --file=$EAP_HOME/bin/adapter-install.cli

インストールは完了です。

次のように、RPMからEAP 6アダプターをインストールします。

Red Hat Enterprise Linux 7では、チャンネルという用語はリポジトリーという用語に置き換えられました。これらの説明では、リポジトリーという用語のみが使用されています。

RPMからEAP 6アダプターをインストールする前に、JBoss EAP 6リポジトリーにサブスクライブする必要があります。

前提条件
  1. Red Hat Subscription Managerを使用して、Red Hat Enterprise Linuxシステムがアカウントに登録されていることを確認してください。詳細は、Red Hat Subscription Management documentationのリンクを参照してください。

  2. すでに別のJBoss EAPリポジトリーに登録している場合は、まずそのリポジトリーから登録を解除する必要があります。

Red Hat Subscription Managerを使用して、次のコマンドを使用してJBoss EAP 6リポジトリーにサブスクライブします。Red Hat Enterprise Linuxのバージョンに応じて、<RHEL_VERSION>を6または7のいずれかに置き換えてください。

$ sudo subscription-manager repos --enable=jb-eap-6-for-rhel-<RHEL_VERSION>-server-rpms

次のコマンドを使用して、OIDC用のEAP 6アダプターをインストールします。

$ sudo yum install keycloak-adapter-sso7_4-eap6
RPMインストールのためのデフォルトのEAP_HOMEパスは、/opt/rh/eap6/root/usr/share/wildflyです。

適切なモジュール・インストール・スクリプトを実行します。

OIDCモジュールの場合は、次のコマンドを実行します。

$ $EAP_HOME/bin/jboss-cli.sh -c --file=$EAP_HOME/bin/adapter-install.cli

インストールは完了です。

2.1.4. JBoss Fuse 6アダプター

Keycloakは JBoss Fuse 6 内で実行されているWebアプリケーションのセキュリティー保護をサポートしています。

JBoss Fuse 6.3.0 Rollup 12は Jetty 9.2 server にバンドルされており、Jettyはさまざまな種類のWebアプリケーションの実行に使用されているため、JBoss Fuse 6はJetty 9アダプターを活用しています。

サポートされているFuse 6のバージョンは最新リリースのみです。以前のバージョンのFuse 6を使用している場合、一部の機能が正しく動作しない可能性があります。特に、 Hawtio との統合は、Fuse 6の以前のバージョンでは機能しません。

Fuseに対して、以下の項目のセキュリティーがサポートされています。

  • Pax Web War Extenderを使用して、FuseにデプロイされたクラシックWARアプリケーション

  • Pax Web Whiteboard Extenderを使用して、FuseにOSGIサービスとしてデプロイされたサーブレット

  • Camel Jetty コンポーネントで動作する Apache Camel Jettyエンドポイント

  • 独自の分離された Jettyエンジン で動作する Apache CXF エンドポイント

  • CXFサーブレットによって提供されるデフォルト・エンジンで実行されている Apache CXF エンドポイント

  • SSHおよびJMXの管理者アクセス

  • Hawtio administration console

Fuse 6内でWebアプリケーションを保護する

最初にKeycloak Karafの機能をインストールする必要があります。次に、セキュリティー保護するアプリケーションの種類に応じた手順を実行する必要があります。参照されているすべてのWebアプリケーションで、Keycloak Jettyオーセンティケーターを、基盤となるJettyサーバーに注入する必要があります。これを達成するための手順は、アプリケーションの種類によって異なります。詳細は以下のとおりです。

始めるのに最適なのは、 fuse ディレクトリー内のKeycloakのサンプルの一部としてバンドルされているFuseのデモを見ることです。ほとんどの手順はテストとデモから理解できるはずです。

Keycloakフィーチャーのインストール

最初に keycloak フィーチャーをJBoss Fuse環境にインストールする必要があります。 keycloak フィーチャーには、Fuseアダプターとサード・パーティーのすべての依存関係が含まれます。Mavenリポジトリーまたはアーカイブからインストールすることができます。

Mavenリポジトリーからのインストール

前提条件として、オンラインでMavenリポジトリーにアクセスできる必要があります。

コミュニティー版の場合、すべてのアーティファクトとサード・パーティーの依存関係がMavenのセントラル・リポジトリーで利用できるため、オンラインにするだけで十分です。

Mavenリポジトリーを使用して、Keycloakフィーチャーをインストールするには、以下の手順を実行します。

  1. JBoss Fuse 6.3.0 Rollup 12を開始します。Karafターミナルで以下のとおりにタイプします。

    features:addurl mvn:org.keycloak/keycloak-osgi-features/11.0.0/xml/features
    features:install keycloak
  2. 以下のようにJetty 9フィーチャーをインストールする必要があるかもしれません。

    features:install keycloak-jetty9-adapter
  3. 以下のようにフィーチャーがインストールされていることを確認します。

features:list | grep keycloak
ZIPバンドルからのインストール

これは、オフラインになっている場合や、Mavenを使用してJARファイルやその他のアーティファクトを取得したくない場合に便利です。

ZIPアーカイブからFuseアダプターをインストールするには、次の手順を実行します。

  1. Keycloak FuseアダプターのZIPアーカイブをダウンロードしてください。

  2. JBoss Fuseのルート・ディレクトリーに解凍します。依存関係にあるファイルは system ディレクトリーの下にインストールされます。既存のすべてのjarファイルが上書きされます。

    JBoss Fuse 6.3.0 Rollup 12には、これを使用します。

    cd /path-to-fuse/jboss-fuse-6.3.0.redhat-254
    unzip -q /path-to-adapter-zip/keycloak-fuse-adapter-11.0.0.zip
  3. Fuseを起動し、Fuse/Karafターミナルで次のコマンドを実行します。

    features:addurl mvn:org.keycloak/keycloak-osgi-features/11.0.0/xml/features
    features:install keycloak
  4. 対応するJettyアダプターをインストールします。アーティファクトはJBoss Fuseの system ディレクトリーから直接利用できるので、Mavenリポジトリーを使う必要はありません。

クラシックWARアプリケーションのセキュリティー保護

WARアプリケーションをセキュリティー保護するために必要な手順は次のとおりです。

  1. /WEB-INF/web.xml ファイルで、次のように必要なものを宣言します。

    • <security-constraint>要素のセキュリティー制約

    • <login-config>要素のログイン設定

    • <security-role>要素のセキュリティー・ロール。

      例:

      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns="http://java.sun.com/xml/ns/javaee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
               version="3.0">
      
          <module-name>customer-portal</module-name>
      
          <welcome-file-list>
              <welcome-file>index.html</welcome-file>
          </welcome-file-list>
      
          <security-constraint>
              <web-resource-collection>
                  <web-resource-name>Customers</web-resource-name>
                  <url-pattern>/customers/*</url-pattern>
              </web-resource-collection>
              <auth-constraint>
                  <role-name>user</role-name>
              </auth-constraint>
          </security-constraint>
      
          <login-config>
              <auth-method>BASIC</auth-method>
              <realm-name>does-not-matter</realm-name>
          </login-config>
      
          <security-role>
              <role-name>admin</role-name>
          </security-role>
          <security-role>
              <role-name>user</role-name>
          </security-role>
      </web-app>
  2. オーセンティケーターを設定した /WEB_INF/jetty-web.xml ファイルを追加してください。

    例:

    <?xml version="1.0"?>
    <!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN"
     "http://www.eclipse.org/jetty/configure_9_0.dtd">
    <Configure class="org.eclipse.jetty.webapp.WebAppContext">
        <Get name="securityHandler">
            <Set name="authenticator">
                <New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
                </New>
            </Set>
        </Get>
    </Configure>
  3. WARの /WEB-INF/ ディレクトリー内に、新しい keycloak.json ファイルを作成します。この設定ファイルの形式は、Javaアダプターの設定のセクションで説明しています。外部アダプターの設定で説明されているように、外部でこのファイルを使用することもできます。

  4. WARアプリケーションが org.keycloak.adapters.jettyMETA-INF/MANIFEST.MF ファイルの Import-Package ヘッダーの下にあるパッケージをインポートしていることを確認してください。プロジェクトで maven-bundle-plugin を使うと、マニフェストにOSGIヘッダーが正しく生成されます。パッケージの "*" による解決は org.keycloak.adapters.jetty パッケージをインポートしないことに注意してください。アプリケーションやBlueprint、Spring記述子では使用されず、 jetty-web.xml ファイルで使用されるためです。

    インポートするパッケージのリストは次のようになります。

    org.keycloak.adapters.jetty;version="11.0.0",
    org.keycloak.adapters;version="11.0.0",
    org.keycloak.constants;version="11.0.0",
    org.keycloak.util;version="11.0.0",
    org.keycloak.*;version="11.0.0",
    *;resolution:=optional
外部アダプターの設定

keycloak.json アダプターの設定ファイルをWARアプリケーションの中にバンドルするのではなく、外部で使用可能にし、命名規則に基づいてロードする場合は、この設定方法を使用してください。

この機能を有効にするには、次のセクションを /WEB_INF/web.xml ファイルに追加してください。

<context-param>
    <param-name>keycloak.config.resolver</param-name>
    <param-value>org.keycloak.adapters.osgi.PathBasedKeycloakConfigResolver</param-value>
</context-param>

そのコンポーネントは、 keycloak.config または karaf.etc のjavaプロパティーを使って基本フォルダーを検索し、設定を探します。そして、見つけたフォルダーの中で <your_web_context>-keycloak.json というファイルを探します。

たとえば、Webアプリケーションにコンテキスト my-portal がある場合、アダプターの設定は $FUSE_HOME/etc/my-portal-keycloak.json ファイルからロードされます。

OSGIサービスとしてデプロイされたサーブレットのセキュリティー保護

クラシックなWARアプリケーションとしてデプロイされていないOSGIバンドルされたプロジェクト内にサーブレット・クラスがある場合、この方式を使用できます。FuseはPax Web Whiteboard Extenderを使用して、サーブレットをWebアプリケーションとしてデプロイします。

Keycloakでサーブレットをセキュリティー保護するには、次の手順を実行します。

  1. KeycloakはPaxWebIntegrationServiceを提供します。これにより、jetty-web.xmlを注入し、アプリケーションのセキュリティー制約を設定できます。アプリケーション内の OSGI-INF/blueprint/blueprint.xml ファイルでそのようなサービスを宣言する必要があります。サーブレットはそれに依存する必要があることに注意してください。設定例は次のとおりです。

    <?xml version="1.0" encoding="UTF-8"?>
    <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0
               http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
    
        <!-- Using jetty bean just for the compatibility with other fuse services -->
        <bean id="servletConstraintMapping" class="org.eclipse.jetty.security.ConstraintMapping">
            <property name="constraint">
                <bean class="org.eclipse.jetty.util.security.Constraint">
                    <property name="name" value="cst1"/>
                    <property name="roles">
                        <list>
                            <value>user</value>
                        </list>
                    </property>
                    <property name="authenticate" value="true"/>
                    <property name="dataConstraint" value="0"/>
                </bean>
            </property>
            <property name="pathSpec" value="/product-portal/*"/>
        </bean>
    
        <bean id="keycloakPaxWebIntegration" class="org.keycloak.adapters.osgi.PaxWebIntegrationService"
              init-method="start" destroy-method="stop">
            <property name="jettyWebXmlLocation" value="/WEB-INF/jetty-web.xml" />
            <property name="bundleContext" ref="blueprintBundleContext" />
            <property name="constraintMappings">
                <list>
                    <ref component-id="servletConstraintMapping" />
                </list>
            </property>
        </bean>
    
        <bean id="productServlet" class="org.keycloak.example.ProductPortalServlet" depends-on="keycloakPaxWebIntegration">
        </bean>
    
        <service ref="productServlet" interface="javax.servlet.Servlet">
            <service-properties>
                <entry key="alias" value="/product-portal" />
                <entry key="servlet-name" value="ProductServlet" />
                <entry key="keycloak.config.file" value="/keycloak.json" />
            </service-properties>
        </service>
    
    </blueprint>
    • プロジェクト内に WEB-INF ディレクトリー(プロジェクトがWebアプリケーションでない場合でも)を用意し、クラシックWARアプリケーションのセクションのように /WEB-INF/jetty-web.xml/WEB-INF/keycloak.json ファイルを作成する必要があります。security-constraintsがblueprint設定ファイルで宣言されているので、 web.xml ファイルは必要ありません。

  2. META-INF/MANIFEST.MFImport-Package は、少なくとも以下のインポートを含んでいなければなりません。

    org.keycloak.adapters.jetty;version="11.0.0",
    org.keycloak.adapters;version="11.0.0",
    org.keycloak.constants;version="11.0.0",
    org.keycloak.util;version="11.0.0",
    org.keycloak.*;version="11.0.0",
    *;resolution:=optional
Apache Camelアプリケーションのセキュリティー保護

KeycloakJettyAuthenticator とsecurityHandlerを追加し、適切なセキュリティー制約を注入することで、 camel-jetty コンポーネントで実装されたApache Camelエンドポイントをセキュリティー保護できます。 OSGI-INF/blueprint/blueprint.xml ファイルを、以下のような設定でCamelアプリケーションに追加できます。ロール、セキュリティー制約のマッピング、およびKeycloakアダプターの設定は、使用する環境と必要性により若干異なる場合があります。

例:

<?xml version="1.0" encoding="UTF-8"?>

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:camel="http://camel.apache.org/schema/blueprint"
           xsi:schemaLocation="
       http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
       http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">

    <bean id="kcAdapterConfig" class="org.keycloak.representations.adapters.config.AdapterConfig">
        <property name="realm" value="demo"/>
        <property name="resource" value="admin-camel-endpoint"/>
        <property name="bearerOnly" value="true"/>
        <property name="authServerUrl" value="http://localhost:8080/auth" />
        <property name="sslRequired" value="EXTERNAL"/>
    </bean>

    <bean id="keycloakAuthenticator" class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
        <property name="adapterConfig" ref="kcAdapterConfig"/>
    </bean>

    <bean id="constraint" class="org.eclipse.jetty.util.security.Constraint">
        <property name="name" value="Customers"/>
        <property name="roles">
            <list>
                <value>admin</value>
            </list>
        </property>
        <property name="authenticate" value="true"/>
        <property name="dataConstraint" value="0"/>
    </bean>

    <bean id="constraintMapping" class="org.eclipse.jetty.security.ConstraintMapping">
        <property name="constraint" ref="constraint"/>
        <property name="pathSpec" value="/*"/>
    </bean>

    <bean id="securityHandler" class="org.eclipse.jetty.security.ConstraintSecurityHandler">
        <property name="authenticator" ref="keycloakAuthenticator" />
        <property name="constraintMappings">
            <list>
                <ref component-id="constraintMapping" />
            </list>
        </property>
        <property name="authMethod" value="BASIC"/>
        <property name="realmName" value="does-not-matter"/>
    </bean>

    <bean id="sessionHandler" class="org.keycloak.adapters.jetty.spi.WrappingSessionHandler">
        <property name="handler" ref="securityHandler" />
    </bean>

    <bean id="helloProcessor" class="org.keycloak.example.CamelHelloProcessor" />

    <camelContext id="blueprintContext"
                  trace="false"
                  xmlns="http://camel.apache.org/schema/blueprint">
        <route id="httpBridge">
            <from uri="jetty:http://0.0.0.0:8383/admin-camel-endpoint?handlers=sessionHandler&amp;matchOnUriPrefix=true" />
            <process ref="helloProcessor" />
            <log message="The message from camel endpoint contains ${body}"/>
        </route>
    </camelContext>

</blueprint>
  • META-INF/MANIFEST.MFImport-Package には、以下のインポートが含まれている必要があります。

javax.servlet;version="[3,4)",
javax.servlet.http;version="[3,4)",
org.apache.camel.*,
org.apache.camel;version="[2.13,3)",
org.eclipse.jetty.security;version="[9,10)",
org.eclipse.jetty.server.nio;version="[9,10)",
org.eclipse.jetty.util.security;version="[9,10)",
org.keycloak.*;version="11.0.0",
org.osgi.service.blueprint,
org.osgi.service.blueprint.container,
org.osgi.service.event,
Camel RestDSL

Camel RestDSLは、流暢な方法でRESTエンドポイントを定義するために使用されるCamelの機能です。しかし、依然として特定の実装クラスを使用し、Keycloakとの統合方法に関する指示を提供する必要があります。

統合機構を設定する方法は、RestDSLで定義されたルートを設定するCamelコンポーネントによって異なります。

次の例は、Jettyコンポーネントを使用して統合を設定する方法を示しています(以前のBlueprintの例で定義されているBeanの一部を参照)。

<bean id="securityHandlerRest" class="org.eclipse.jetty.security.ConstraintSecurityHandler">
    <property name="authenticator" ref="keycloakAuthenticator" />
    <property name="constraintMappings">
        <list>
            <ref component-id="constraintMapping" />
        </list>
    </property>
    <property name="authMethod" value="BASIC"/>
    <property name="realmName" value="does-not-matter"/>
</bean>

<bean id="sessionHandlerRest" class="org.keycloak.adapters.jetty.spi.WrappingSessionHandler">
    <property name="handler" ref="securityHandlerRest" />
</bean>


<camelContext id="blueprintContext"
              trace="false"
              xmlns="http://camel.apache.org/schema/blueprint">

    <restConfiguration component="jetty" contextPath="/restdsl"
                       port="8484">
        <!--the link with Keycloak security handlers happens here-->
        <endpointProperty key="handlers" value="sessionHandlerRest"></endpointProperty>
        <endpointProperty key="matchOnUriPrefix" value="true"></endpointProperty>
    </restConfiguration>

    <rest path="/hello" >
        <description>Hello rest service</description>
        <get uri="/{id}" outType="java.lang.String">
            <description>Just an helllo</description>
            <to uri="direct:justDirect" />
        </get>

    </rest>

    <route id="justDirect">
        <from uri="direct:justDirect"/>
        <process ref="helloProcessor" />
        <log message="RestDSL correctly invoked ${body}"/>
        <setBody>
            <constant>(__This second sentence is returned from a Camel RestDSL endpoint__)</constant>
        </setBody>
    </route>

</camelContext>
独立したJettyエンジンでのApache CXFエンドポイントのセキュリティー保護

別のJettyエンジンでKeycloakによって保護されたCXFエンドポイントを実行するには、以下の手順に沿って行います。

  1. アプリケーションに META-INF/spring/beans.xml を追加し、 KeycloakJettyAuthenticator を注入したJettyの SecurityHandlerhttpj:engine-factory を宣言してください。CFXのJAX-WSアプリケーションの設定は、次のようになります。

    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:jaxws="http://cxf.apache.org/jaxws"
           xmlns:httpj="http://cxf.apache.org/transports/http-jetty/configuration"
           xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
            http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
            http://cxf.apache.org/transports/http-jetty/configuration http://cxf.apache.org/schemas/configuration/http-jetty.xsd">
    
        <import resource="classpath:META-INF/cxf/cxf.xml" />
    
        <bean id="kcAdapterConfig" class="org.keycloak.representations.adapters.config.AdapterConfig">
            <property name="realm" value="demo"/>
            <property name="resource" value="custom-cxf-endpoint"/>
            <property name="bearerOnly" value="true"/>
            <property name="authServerUrl" value="http://localhost:8080/auth" />
            <property name="sslRequired" value="EXTERNAL"/>
        </bean>
    
        <bean id="keycloakAuthenticator" class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
            <property name="adapterConfig">
                <ref local="kcAdapterConfig" />
            </property>
        </bean>
    
        <bean id="constraint" class="org.eclipse.jetty.util.security.Constraint">
            <property name="name" value="Customers"/>
            <property name="roles">
                <list>
                    <value>user</value>
                </list>
            </property>
            <property name="authenticate" value="true"/>
            <property name="dataConstraint" value="0"/>
        </bean>
    
        <bean id="constraintMapping" class="org.eclipse.jetty.security.ConstraintMapping">
            <property name="constraint" ref="constraint"/>
            <property name="pathSpec" value="/*"/>
        </bean>
    
        <bean id="securityHandler" class="org.eclipse.jetty.security.ConstraintSecurityHandler">
            <property name="authenticator" ref="keycloakAuthenticator" />
            <property name="constraintMappings">
                <list>
                    <ref local="constraintMapping" />
                </list>
            </property>
            <property name="authMethod" value="BASIC"/>
            <property name="realmName" value="does-not-matter"/>
        </bean>
    
        <httpj:engine-factory bus="cxf" id="kc-cxf-endpoint">
            <httpj:engine port="8282">
                <httpj:handlers>
                    <ref local="securityHandler" />
                </httpj:handlers>
                <httpj:sessionSupport>true</httpj:sessionSupport>
            </httpj:engine>
        </httpj:engine-factory>
    
        <jaxws:endpoint
                        implementor="org.keycloak.example.ws.ProductImpl"
                        address="http://localhost:8282/ProductServiceCF" depends-on="kc-cxf-endpoint" />
    
    </beans>

    CXFのJAX-RSアプリケーションの場合、engine-factortyに依存するエンドポイントの設定にのみ違いがある可能性があります。

    <jaxrs:server serviceClass="org.keycloak.example.rs.CustomerService" address="http://localhost:8282/rest"
        depends-on="kc-cxf-endpoint">
        <jaxrs:providers>
            <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider" />
        </jaxrs:providers>
    </jaxrs:server>
  2. META-INF/MANIFEST.MFImport-Package は以下のインポートを含まなければなりません。

META-INF.cxf;version="[2.7,3.2)",
META-INF.cxf.osgi;version="[2.7,3.2)";resolution:=optional,
org.apache.cxf.bus;version="[2.7,3.2)",
org.apache.cxf.bus.spring;version="[2.7,3.2)",
org.apache.cxf.bus.resource;version="[2.7,3.2)",
org.apache.cxf.transport.http;version="[2.7,3.2)",
org.apache.cxf.*;version="[2.7,3.2)",
org.springframework.beans.factory.config,
org.eclipse.jetty.security;version="[9,10)",
org.eclipse.jetty.util.security;version="[9,10)",
org.keycloak.*;version="11.0.0"
デフォルトのJettyエンジンでのApache CXFエンドポイントの保護

いくつかのサービスは、起動時にデプロイされたサーブレットを自動的に提供します。そのようなサービスの1つは、 http://localhost:8181/cxf コンテキストで実行されているCXFサーブレットです。そのようなエンドポイントを保護することは複雑になる可能性があります。Keycloakが現在使用しているアプローチの1つは ServletReregistrationService で、これは組み込みのサーブレットを起動時にアンデプロイし、Keycloakによってセキュリティー保護されたコンテキストに再デプロイできるようにします。

アプリケーション内の設定ファイル OSGI-INF/blueprint/blueprint.xml は、以下のようになります。JAX-RSの customerservice エンドポイント(アプリケーション固有のエンドポイント)を追加しますが、もっと重要なのは、 /cxf コンテキスト全体をセキュリティー保護することです。

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs"
           xsi:schemaLocation="
                http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
                http://cxf.apache.org/blueprint/jaxrs http://cxf.apache.org/schemas/blueprint/jaxrs.xsd">

    <!-- JAXRS Application -->

    <bean id="customerBean" class="org.keycloak.example.rs.CxfCustomerService" />

    <jaxrs:server id="cxfJaxrsServer" address="/customerservice">
        <jaxrs:providers>
            <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider" />
        </jaxrs:providers>
        <jaxrs:serviceBeans>
            <ref component-id="customerBean" />
        </jaxrs:serviceBeans>
    </jaxrs:server>


    <!-- Securing of whole /cxf context by unregister default cxf servlet from paxweb and re-register with applied security constraints -->

    <bean id="cxfConstraintMapping" class="org.eclipse.jetty.security.ConstraintMapping">
        <property name="constraint">
            <bean class="org.eclipse.jetty.util.security.Constraint">
                <property name="name" value="cst1"/>
                <property name="roles">
                    <list>
                        <value>user</value>
                    </list>
                </property>
                <property name="authenticate" value="true"/>
                <property name="dataConstraint" value="0"/>
            </bean>
        </property>
        <property name="pathSpec" value="/cxf/*"/>
    </bean>

    <bean id="cxfKeycloakPaxWebIntegration" class="org.keycloak.adapters.osgi.PaxWebIntegrationService"
          init-method="start" destroy-method="stop">
        <property name="bundleContext" ref="blueprintBundleContext" />
        <property name="jettyWebXmlLocation" value="/WEB-INF/jetty-web.xml" />
        <property name="constraintMappings">
            <list>
                <ref component-id="cxfConstraintMapping" />
            </list>
        </property>
    </bean>

    <bean id="defaultCxfReregistration" class="org.keycloak.adapters.osgi.ServletReregistrationService" depends-on="cxfKeycloakPaxWebIntegration"
          init-method="start" destroy-method="stop">
        <property name="bundleContext" ref="blueprintBundleContext" />
        <property name="managedServiceReference">
            <reference interface="org.osgi.service.cm.ManagedService" filter="(service.pid=org.apache.cxf.osgi)" timeout="5000"  />
        </property>
    </bean>

</blueprint>

その結果、デフォルトのCXF HTTPの宛先で実行されている他のすべてのCXFサービスも保護されます。同様に、アプリケーションがアンデプロイされると、 /cxf コンテキスト全体がセキュリティー保護されなくなります。このため、独立したJettyエンジンでのApache CXFエンドポイントの保護で説明されているように、独自のJettyエンジンをアプリケーションに使用すると、個々のアプリケーションのセキュリティーをより詳細に制御できます。

  • WEB-INF ディレクトリーはプロジェクト内にある必要があります(プロジェクトがWebアプリケーションでない場合でも)。 また、クラシックWARアプリケーションと同様に、 /WEB-INF/jetty-web.xml および /WEB-INF/keycloak.json ファイルを編集する必要があります。セキュリティー制約がblueprint設定ファイルで宣言されているので、 web.xml ファイルは必要ないことに注意してください。

  • META-INF/MANIFEST.MFImport-Package は以下のインポートを含まなければなりません。

META-INF.cxf;version="[2.7,3.2)",
META-INF.cxf.osgi;version="[2.7,3.2)";resolution:=optional,
org.apache.cxf.transport.http;version="[2.7,3.2)",
org.apache.cxf.*;version="[2.7,3.2)",
com.fasterxml.jackson.jaxrs.json;version="[2.5,3)",
org.eclipse.jetty.security;version="[9,10)",
org.eclipse.jetty.util.security;version="[9,10)",
org.keycloak.*;version="11.0.0",
org.keycloak.adapters.jetty;version="11.0.0",
*;resolution:=optional
Fuse管理サービスのセキュリティー保護
FuseターミナルへのSSH認証の使用

Keycloakは、主にWebアプリケーションの認証のユースケースを扱います。ただし、他のWebサービスやアプリケーションがKeycloakで保護されている場合は、KeycloakのクレデンシャルでSSHなどのWeb以外の管理サービスを保護するのが最善の方法です。これは、Keycloakへのリモート接続を許可し、リソース・オーナー・パスワード・クレデンシャルに基づいてクレデンシャルを検証するJAASログイン・モジュールを使用して実行できます。

SSH認証を有効にするには、次の手順を実行します。

  1. Keycloakでは、SSH認証に使用されるクライアント(たとえば、 ssh-jmx-admin-client )を作成します。このクライアントでは、 Direct Access Grants EnabledOn に選択されている必要があります。

  2. $FUSE_HOME/etc/org.apache.karaf.shell.cfg ファイルで、次のとおりにこのプロパティーを更新または指定します。

    sshRealm=keycloak
  3. Add the $FUSE_HOME/etc/keycloak-direct-access.json file with content similar to the following (based on your environment and Keycloak client settings):

    {
        "realm": "demo",
        "resource": "ssh-jmx-admin-client",
        "ssl-required" : "external",
        "auth-server-url" : "http://localhost:8080/auth",
        "credentials": {
            "secret": "password"
        }
    }

    このファイルでは、JAASの DirectAccessGrantsLoginModule がSSH認証のために keycloak JAASレルムから使用するクライアント・アプリケーションの設定を指定します。

  4. Fuseを起動し、 keycloak JAASレルムをインストールしてください。最も簡単な方法は、JAASレルムがあらかじめ定義された keycloak-jaas フィーチャーをインストールすることです。より高いランクの独自 keycloak JAASレルムを使用して、フィーチャーに定義済みのレルムをオーバーライドすることができます。詳細については、 JBoss Fuse documentationを参照してください。

    Fuseターミナルで次のコマンドを使用します。

    features:addurl mvn:org.keycloak/keycloak-osgi-features/11.0.0/xml/features
    features:install keycloak-jaas
  5. SSHで admin ユーザーとしてログインするには、ターミナルで次のように入力します。

    ssh -o PubkeyAuthentication=no -p 8101 admin@localhost
  6. パスワードは password でログインしてください。

最近のオペレーティング・システムでは、SSHコマンドの-oオプションを -o HostKeyAlgorithms=+ssh-dss として使用する必要があるかもしれません。これは、最近のSSHクライアントではデフォルトで ssh-dss アルゴリズムを使用できないためです。しかし、現在JBoss Fuse 6.3.0 Rollup 12ではデフォルトで使用されています。

ユーザーはすべての操作を実行するために、レルムロール admin を持つ必要があることに注意してください。また、操作のサブセットを実行するためには別のロールが必要となります(たとえば、読み取り専用のKarafコマンドのみを実行するようにユーザーを制限する viewer ロール)。利用可能なロールは、 $FUSE_HOME/etc/org.apache.karaf.shell.cfg または $FUSE_HOME/etc/system.properties で設定されます。

JMX認証の使用

jconsoleまたは別の外部ツールを使用してRMI経由でJMXにリモート接続する場合は、JMX認証が必要になることがあります。それ以外の場合は、デフォルトでhaolt.ioにjolokiaエージェントがインストールされているので、hawt.io/jolokiaを使用する方がよいでしょう。詳細については、Hawtio Admin Consoleを参照してください。

JMX認証を使用するには、次の手順を実行します。

  1. $FUSE_HOME/etc/org.apache.karaf.management.cfg ファイルで、 jmxRealm プロパティーを次のように変更します。

    jmxRealm=keycloak
  2. 上記のSSHのセクションで説明したように、 keycloak-jaas フィーチャーをインストールし、 $FUSE_HOME/etc/keycloak-direct-access.json ファイルを設定してください。

  3. jconsoleでは、次のようなURLを使用できます。

service:jmx:rmi://localhost:44444/jndi/rmi://localhost:1099/karaf-root

クレデンシャルはadmin/password(利用環境の管理者権限を持つユーザー次第)です。

Hawtio管理コンソールのセキュリティー保護

Keycloakを使用して、Hawtio管理コンソールをセキュリティー保護するには、以下の手順を実行します。

  1. これらのプロパティーを $FUSE_HOME/etc/system.properties ファイルに追加します。

    hawtio.keycloakEnabled=true
    hawtio.realm=keycloak
    hawtio.keycloakClientConfig=file://${karaf.base}/etc/keycloak-hawtio-client.json
    hawtio.rolePrincipalClasses=org.keycloak.adapters.jaas.RolePrincipal,org.apache.karaf.jaas.boot.principal.RolePrincipal
  2. 利用レルムのKeycloak管理コンソールにクライアントを作成します。たとえば、Keycloakの demo レルムでクライアント hawtio-client を作成し、アクセス・タイプとして public を指定し、Hawtio: http://localhost:8181/hawtio/* を指すリダイレクトURIを指定します。また、対応するWeb Origin(この場合は、 http://localhost:8181)も設定する必要があります。

  3. $FUSE_HOME/etc ディレクトリーに keycloak-hawtio-client.json ファイルを作成します。このファイルは、以下の例のような内容で作成します。Keycloak環境に応じて、 realmresourceauth-server-url の各プロパティーを変更してください。 resource プロパティーは前のステップで作成されたクライアントを指し示さなければなりません。このファイルは、クライアント(Hawtio JavaScriptアプリケーション)側で使用されます。

    {
      "realm" : "demo",
      "resource" : "hawtio-client",
      "auth-server-url" : "http://localhost:8080/auth",
      "ssl-required" : "external",
      "public-client" : true
    }
  4. $FUSE_HOME/etc ディレクトリーに keycloak-hawtio.json ファイルを作成します。このファイルは、以下の例のような内容です。Keycloakの環境に応じて realmauth-server-url プロパティーを変更してください。このファイルは、サーバー(JAASログイン・モジュール)側のアダプターによって使用されます。

    {
      "realm" : "demo",
      "resource" : "jaas",
      "bearer-only" : true,
      "auth-server-url" : "http://localhost:8080/auth",
      "ssl-required" : "external",
      "use-resource-role-mappings": false,
      "principal-attribute": "preferred_username"
    }
  5. Keycloakフィーチャーをまだインストールしていない場合は、JBoss Fuse 6.3.0 Rollup 12を起動し、インストールしてください。Karafターミナルのコマンドはこの例に似ています。

    features:addurl mvn:org.keycloak/keycloak-osgi-features/11.0.0/xml/features
    features:install keycloak
  6. http://localhost:8181/hawtio に移動し、Keycloakのレルムからユーザーとしてログインします。

    Hawtioに対して正常に認証するには、ユーザーが適切なレルムロールを持っている必要があることに注意してください。利用可能なロールは、 hawtio.roles$FUSE_HOME/etc/system.properties ファイルで設定されます。

JBoss EAP 6.4上のHawtioのセキュリティー保護

JBoss EAP 6.4サーバーでHawtioを実行するには、以下の手順を実行します。

  1. 前のセクション(Hawtio管理コンソールをセキュリティー保護する)で説明したように、Keycloakを設定します。次のように仮定します。

    • Keycloakのレルムのデモとクライアント hawtio-client を持っている

    • Keycloakが localhost:8080 で動作している

    • 展開されたHawtioがあるJBoss EAP 6.4サーバーは localhost:8181 で動作します。このサーバーのディレクトリーは、次のステップでは、 $EAP_HOME と呼ばれます。

  2. hawtio-wildfly-1.4.0.redhat-630396.war アーカイブを $EAP_HOME/standalone/configuration ディレクトリーにコピーします。Hawtioの導入の詳細については、 Fuse Hawtio documentationを参照してください。

  3. 上記内容の keycloak-hawtio.json ファイルと keycloak-hawtio-client.json ファイルを $EAP_HOME/standalone/configuration ディレクトリーにコピーします。

  4. JBossアダプターのドキュメントの説明に従って、Keycloakアダプター・サブシステムをJBoss EAP 6.4サーバーにインストールします。

  5. $EAP_HOME/standalone/configuration/standalone.xml ファイルで、次の例のようにシステムのプロパティーを設定します。

    <extensions>
    ...
    </extensions>
    
    <system-properties>
        <property name="hawtio.authenticationEnabled" value="true" />
        <property name="hawtio.realm" value="hawtio" />
        <property name="hawtio.roles" value="admin,viewer" />
        <property name="hawtio.rolePrincipalClasses" value="org.keycloak.adapters.jaas.RolePrincipal" />
        <property name="hawtio.keycloakEnabled" value="true" />
        <property name="hawtio.keycloakClientConfig" value="${jboss.server.config.dir}/keycloak-hawtio-client.json" />
        <property name="hawtio.keycloakServerConfig" value="${jboss.server.config.dir}/keycloak-hawtio.json" />
    </system-properties>
  6. Hawtioレルムを同じファイルの security-domains セクションに追加します。

    <security-domain name="hawtio" cache-type="default">
        <authentication>
            <login-module code="org.keycloak.adapters.jaas.BearerTokenLoginModule" flag="required">
                <module-option name="keycloak-config-file" value="${hawtio.keycloakServerConfig}"/>
            </login-module>
        </authentication>
    </security-domain>
  7. hawtiosecure-deployment セクションをアダプター・サブシステムに追加してください。これにより、Hawtio WARがJAASログイン・モジュール・クラスを見つけることができます。

    <subsystem xmlns="urn:jboss:domain:keycloak:1.1">
        <secure-deployment name="hawtio-wildfly-1.4.0.redhat-630396.war" />
    </subsystem>
  8. HawtioとJBoss EAP 6.4サーバーを再起動します。

    cd $EAP_HOME/bin
    ./standalone.sh -Djboss.socket.binding.port-offset=101
  9. Hawtio( http://localhost:8181/hawtio )へアクセスしてください。Keycloakによってセキュリティー保護されています。

2.1.5. JBoss Fuse 7アダプター

Keycloakは JBoss Fuse 7 内で実行されているWebアプリケーションのセキュリティー保護をサポートしています。

JBoss Fuse 7は、基本的に同等であるUndertowアダプターを利用しています。 EAP 7 / WildFlyアダプター JBoss Fuse 7.4.0は Undertow HTTP engine にバンドルされており、Undertowはさまざまな種類のWebアプリケーションの実行に使用されています。

サポートされているFuse 7のバージョンは最新リリースのみです。以前のバージョンのFuse 7を使用している場合、一部の機能が正しく動作しない可能性があります。特に、7.0.1より前のバージョンのFuse 7では、統合はまったく機能しません。

Fuseに対して、以下の項目のセキュリティーがサポートされています。

  • Pax Web War Extenderを使用して、FuseにデプロイされたクラシックWARアプリケーション

  • Pax Web Whiteboard Extenderを使用してFuseにOSGIサービスとしてデプロイされたサーブレットと、さらに標準のOSGi Enterprise HTTP Serviceであるorg.osgi.service.http.HttpService#registerServlet()によって登録されたサーブレット

  • Camel Undertow コンポーネントで動作する Apache Camel Undertowエンドポイント

  • 独自の分離されたUndertowエンジンで動作する Apache CXF エンドポイント

  • CXFサーブレットによって提供されるデフォルト・エンジンで実行されている Apache CXF エンドポイント

  • SSHおよびJMXの管理者アクセス

  • Hawtio administration console

Fuse 7内でWebアプリケーションを保護する

最初にKeycloak Karafのフィーチャーをインストールする必要があります。次に、セキュリティー保護するアプリケーションの種類に応じた手順を実行する必要があります。参照されているすべてのWebアプリケーションで、Keycloak Undertow認証メカニズムを、基盤となるWebサーバーに注入する必要があります。これを達成するための手順は、アプリケーションの種類によって異なります。詳細は以下のとおりです。

始めるのに最適なのは、 fuse ディレクトリー内のKeycloakのサンプルの一部としてバンドルされているFuseのデモを見ることです。ほとんどの手順はテストとデモから理解できるはずです。

Keycloakフィーチャーのインストール

最初に keycloak-pax-http-undertowkeycloak-jaas のフィーチャーをJBoss Fuse環境にインストールする必要があります。 keycloak-pax-http-undertow フィーチャーには、Fuseアダプターとサードパーティーのすべての依存関係が含まれます。 keycloak-jaas には、SSHとJMX認証のためにレルムで使用されるJAASモジュールが含まれています。Mavenリポジトリーまたはアーカイブからインストールすることができます。

Mavenリポジトリーからのインストール

前提条件として、オンラインでMavenリポジトリーにアクセスできる必要があります。

コミュニティー版の場合、すべてのアーティファクトとサード・パーティーの依存関係がMavenのセントラル・リポジトリーで利用できるため、オンラインにするだけで十分です。

Mavenリポジトリーを使用して、Keycloakフィーチャーをインストールするには、以下の手順を実行します。

  1. JBoss Fuse 7.4.0を開始します。Karafターミナルで以下のとおりにタイプします。

    feature:repo-add mvn:org.keycloak/keycloak-osgi-features/11.0.0/xml/features
    feature:install keycloak-pax-http-undertow keycloak-jaas
  2. 以下のようにUndertowフィーチャーをインストールする必要もあるかもしれません。

    feature:install pax-http-undertow
  3. 以下のようにフィーチャーがインストールされていることを確認します。

feature:list | grep keycloak
ZIPバンドルからのインストール

これは、オフラインになっている場合や、Mavenを使用してJARファイルやその他のアーティファクトを取得したくない場合に便利です。

ZIPアーカイブからFuseアダプターをインストールするには、次の手順を実行します。

  1. Keycloak FuseアダプターのZIPアーカイブをダウンロードしてください。

  2. JBoss Fuseのルート・ディレクトリーに解凍します。依存関係にあるファイルは system ディレクトリーの下にインストールされます。既存のすべてのjarファイルが上書きされます。

    JBoss Fuse 7.4.0には、これを使用します。

    cd /path-to-fuse/fuse-karaf-7.z
    unzip -q /path-to-adapter-zip/keycloak-fuse-adapter-11.0.0.zip
  3. Fuseを起動し、Fuse/Karafターミナルで次のコマンドを実行します。

    feature:repo-add mvn:org.keycloak/keycloak-osgi-features/11.0.0/xml/features
    feature:install keycloak-pax-http-undertow keycloak-jaas
  4. 対応するUndertowアダプターをインストールします。アーティファクトはJBoss Fuseの system ディレクトリーから直接利用できるので、Mavenリポジトリーを使う必要はありません。

クラシックWARアプリケーションのセキュリティー保護

WARアプリケーションをセキュリティー保護するために必要な手順は次のとおりです。

  1. /WEB-INF/web.xml ファイルで、次のように必要なものを宣言します。

    • <security-constraint>要素のセキュリティー制約

    • <login-config> 要素内のログイン設定。 <auth-method>KEYCLOAK であることを確認してください。

    • <security-role> 要素のセキュリティー・ロール。

      例:

      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns="http://java.sun.com/xml/ns/javaee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
               version="3.0">
      
          <module-name>customer-portal</module-name>
      
          <welcome-file-list>
              <welcome-file>index.html</welcome-file>
          </welcome-file-list>
      
          <security-constraint>
              <web-resource-collection>
                  <web-resource-name>Customers</web-resource-name>
                  <url-pattern>/customers/*</url-pattern>
              </web-resource-collection>
              <auth-constraint>
                  <role-name>user</role-name>
              </auth-constraint>
          </security-constraint>
      
          <login-config>
              <auth-method>KEYCLOAK</auth-method>
              <realm-name>does-not-matter</realm-name>
          </login-config>
      
          <security-role>
              <role-name>admin</role-name>
          </security-role>
          <security-role>
              <role-name>user</role-name>
          </security-role>
      </web-app>
  2. WARの /WEB-INF/ ディレクトリー内に、新しい keycloak.json ファイルを作成します。この設定ファイルの形式は、Javaアダプターの設定のセクションで説明しています。外部アダプターの設定で説明されているように、外部でこのファイルを使用することもできます。

    例:

    {
        "realm": "demo",
        "resource": "customer-portal",
        "auth-server-url": "http://localhost:8080/auth",
        "ssl-required" : "external",
        "credentials": {
            "secret": "password"
        }
    }
  3. Fuse 6アダプターとは異なり、MANIFEST.MFには特別なOSGiのインポートは必要ありません。

設定リゾルバー

keycloak.json アダプター設定ファイルはバンドル内か(デフォルトの動作)、ファイルシステム上のディレクトリーに保存することができます。設定ファイルの実際のソースを指定するには、 keycloak.config.resolver 配備パラメーターを目的の設定リゾルバークラスに設定します。たとえば、古典的なWARアプリケーションでは、 web.xml ファイルに keycloak.config.resolver コンテキスト・パラメーターを以下のように設定します。

<context-param>
    <param-name>keycloak.config.resolver</param-name>
    <param-value>org.keycloak.adapters.osgi.PathBasedKeycloakConfigResolver</param-value>
</context-param>

keycloak.config.resolver には、次のリゾルバーがあります。

org.keycloak.adapters.osgi.BundleBasedKeycloakConfigResolver

これがデフォルトのリゾルバーです。設定ファイルは、セキュリティー保護されているOSGiバンドル内にあります。デフォルトでは、 WEB-INF/keycloak.json という名前のファイルをロードしますが、このファイル名は configLocation プロパティーで設定できます。

org.keycloak.adapters.osgi.PathBasedKeycloakConfigResolver

このリゾルバーは、 keycloak.config システム・プロパティーで指定されたフォルダーの中で <your_web_context>-keycloak.json というファイルを探します。 keycloak.config が設定されていなければ、代わりに karaf.etc システム・プロパティーが使われます。

たとえば、Webアプリケーションがコンテキスト my-portal にデプロイされている場合、アダプターの設定は ${keycloak.config}/my-portal-keycloak.json ファイルか ${karaf.etc}/my-portal-keycloak.json のいずれかからロードされます。

org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver

このリゾルバーは、上記の PathBasedKeycloakConfigResolver に似ています。与えられたURIパスに対して、設定の場所が手当たり次第にチェックされます。

たとえば、URIが /my/web-app/context の場合、設定場所が存在するまで、次のものが存在するかどうかが検索されます。

  • ${karaf.etc}/my-web-app-context-keycloak.json

  • ${karaf.etc}/my-web-app-keycloak.json

  • ${karaf.etc}/my-keycloak.json

  • ${karaf.etc}/keycloak.json

OSGIサービスとしてデプロイされたサーブレットのセキュリティー保護

クラシックなWARアプリケーションとしてデプロイされていないOSGIバンドルされたプロジェクト内にサーブレット・クラスがある場合、この方式を使用できます。FuseはPax Web Whiteboard Extenderを使用して、サーブレットをWebアプリケーションとしてデプロイします。

Keycloakでサーブレットをセキュリティー保護するには、次の手順を実行します。

  1. Keycloakは org.keycloak.adapters.osgi.undertow.PaxWebIntegrationService を提供します。これにより、アプリケーションに認証方法とセキュリティー制約の設定を設定できます。アプリケーション内の OSGI-INF/blueprint/blueprint.xml ファイルでそのようなサービスを宣言する必要があります。サーブレットはそれに依存する必要があることに注意してください。設定例は次のとおりです。

    <?xml version="1.0" encoding="UTF-8"?>
    <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
    
        <bean id="servletConstraintMapping" class="org.keycloak.adapters.osgi.PaxWebSecurityConstraintMapping">
            <property name="roles">
                <list>
                    <value>user</value>
                </list>
            </property>
            <property name="authentication" value="true"/>
            <property name="url" value="/product-portal/*"/>
        </bean>
    
        <!-- This handles the integration and setting the login-config and security-constraints parameters -->
        <bean id="keycloakPaxWebIntegration" class="org.keycloak.adapters.osgi.undertow.PaxWebIntegrationService"
              init-method="start" destroy-method="stop">
            <property name="bundleContext" ref="blueprintBundleContext" />
            <property name="constraintMappings">
                <list>
                    <ref component-id="servletConstraintMapping" />
                </list>
            </property>
        </bean>
    
        <bean id="productServlet" class="org.keycloak.example.ProductPortalServlet" depends-on="keycloakPaxWebIntegration" />
    
        <service ref="productServlet" interface="javax.servlet.Servlet">
            <service-properties>
                <entry key="alias" value="/product-portal" />
                <entry key="servlet-name" value="ProductServlet" />
                <entry key="keycloak.config.file" value="/keycloak.json" />
            </service-properties>
        </service>
    </blueprint>
    • プロジェクト内に WEB-INF ディレクトリー(プロジェクトがWebアプリケーションでない場合でも)を用意し、クラシックWARアプリケーションのセクションのような /WEB-INF/keycloak.json ファイルを作成する必要があります。security-constraintsがblueprint設定ファイルで宣言されているので、 web.xml ファイルは必要ありません。

  2. Fuse 6アダプターとは異なり、MANIFEST.MFには特別なOSGiのインポートは必要ありません。

Apache Camelアプリケーションのセキュリティー保護

camel-undertowコンポーネントで実装されたApache Camelエンドポイントをセキュリティー保護するには、適切なセキュリティー制約をBlueprintを介して注入し、使用されるコンポーネントを undertow-keycloak に更新します。以下のような設定で、 OSGI-INF/blueprint/blueprint.xml ファイルをCamelアプリケーションに追加する必要があります。ロール、セキュリティー制約のマッピング、およびアダプターの設定は、使用する環境と必要性により若干異なる場合があります。

標準の undertow コンポーネントと比較して、 undertow-keycloak コンポーネントは次の2つの新しいプロパティーを追加します。

  • configResolver は、Keycloakアダプター設定を提供するリゾルバービーンです。利用可能なリゾルバーは、設定リゾルバーセクションにリストされています。

  • allowedRoles はカンマで区切られたロールのリストです。サービスにアクセスするユーザーは、アクセスを許可されるロールを少なくとも1つは持っていなければなりません。

例:

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:camel="http://camel.apache.org/schema/blueprint"
           xsi:schemaLocation="
       http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
       http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint-2.17.1.xsd">

    <bean id="keycloakConfigResolver" class="org.keycloak.adapters.osgi.BundleBasedKeycloakConfigResolver" >
        <property name="bundleContext" ref="blueprintBundleContext" />
    </bean>

    <bean id="helloProcessor" class="org.keycloak.example.CamelHelloProcessor" />

    <camelContext id="blueprintContext"
                  trace="false"
                  xmlns="http://camel.apache.org/schema/blueprint">

        <route id="httpBridge">
            <from uri="undertow-keycloak:http://0.0.0.0:8383/admin-camel-endpoint?matchOnUriPrefix=true&amp;configResolver=#keycloakConfigResolver&amp;allowedRoles=admin" />
            <process ref="helloProcessor" />
            <log message="The message from camel endpoint contains ${body}"/>
        </route>

    </camelContext>

</blueprint>
  • META-INF/MANIFEST.MFImport-Package には、以下のインポートが含まれている必要があります。

javax.servlet;version="[3,4)",
javax.servlet.http;version="[3,4)",
javax.net.ssl,
org.apache.camel.*,
org.apache.camel;version="[2.13,3)",
io.undertow.*,
org.keycloak.*;version="11.0.0",
org.osgi.service.blueprint,
org.osgi.service.blueprint.container
Camel RestDSL

Camel RestDSLは、流暢な方法でRESTエンドポイントを定義するために使用されるCamelの機能です。しかし、依然として特定の実装クラスを使用し、Keycloakとの統合方法に関する指示を提供する必要があります。

統合機構を設定する方法は、RestDSLで定義されたルートを設定するCamelコンポーネントによって異なります。

次の例は、 undertow-keycloak コンポーネントを使用して統合を設定する方法を示しています。前回のBlueprintの例で定義されたBeanのいくつかを参照しています。

<camelContext id="blueprintContext"
              trace="false"
              xmlns="http://camel.apache.org/schema/blueprint">

    <!--the link with Keycloak security handlers happens by using undertow-keycloak component -->
    <restConfiguration apiComponent="undertow-keycloak" contextPath="/restdsl" port="8484">
        <endpointProperty key="configResolver" value="#keycloakConfigResolver" />
        <endpointProperty key="allowedRoles" value="admin,superadmin" />
    </restConfiguration>

    <rest path="/hello" >
        <description>Hello rest service</description>
        <get uri="/{id}" outType="java.lang.String">
            <description>Just a hello</description>
            <to uri="direct:justDirect" />
        </get>

    </rest>

    <route id="justDirect">
        <from uri="direct:justDirect"/>
        <process ref="helloProcessor" />
        <log message="RestDSL correctly invoked ${body}"/>
        <setBody>
            <constant>(__This second sentence is returned from a Camel RestDSL endpoint__)</constant>
        </setBody>
    </route>

</camelContext>
分離されたUndertowエンジンでのApache CXFエンドポイントのセキュリティー保護

分離されたUndertowエンジンで、Keycloakによってセキュリティー保護されたCXFエンドポイントを実行するには、以下の手順を実行します。

  1. アプリケーションに OSGI-INF/blueprint/blueprint.xml を追加し、その中にCamelの設定と同様の適切な設定リゾルバービーンを追加します。 httpu:engine-factory の中で、そのCamelの設定を使って org.keycloak.adapters.osgi.undertow.CxfKeycloakAuthHandler ハンドラーを宣言します。CFX JAX-WSアプリケーションの設定は、次のようになります。

    <?xml version="1.0" encoding="UTF-8"?>
    <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:jaxws="http://cxf.apache.org/blueprint/jaxws"
               xmlns:cxf="http://cxf.apache.org/blueprint/core"
               xmlns:httpu="http://cxf.apache.org/transports/http-undertow/configuration".
               xsi:schemaLocation="
          http://cxf.apache.org/transports/http-undertow/configuration http://cxf.apache.org/schemas/configuration/http-undertow.xsd
          http://cxf.apache.org/blueprint/core http://cxf.apache.org/schemas/blueprint/core.xsd
          http://cxf.apache.org/blueprint/jaxws http://cxf.apache.org/schemas/blueprint/jaxws.xsd">
    
        <bean id="keycloakConfigResolver" class="org.keycloak.adapters.osgi.BundleBasedKeycloakConfigResolver" >
            <property name="bundleContext" ref="blueprintBundleContext" />
        </bean>
    
        <httpu:engine-factory bus="cxf" id="kc-cxf-endpoint">
            <httpu:engine port="8282">
                <httpu:handlers>
                    <bean class="org.keycloak.adapters.osgi.undertow.CxfKeycloakAuthHandler">
                        <property name="configResolver" ref="keycloakConfigResolver" />
                    </bean>
                </httpu:handlers>
            </httpu:engine>
        </httpu:engine-factory>
    
        <jaxws:endpoint implementor="org.keycloak.example.ws.ProductImpl"
                        address="http://localhost:8282/ProductServiceCF" depends-on="kc-cxf-endpoint"/>
    
    </blueprint>

    CXFのJAX-RSアプリケーションの場合、engine-factortyに依存するエンドポイントの設定にのみ違いがある可能性があります。

    <jaxrs:server serviceClass="org.keycloak.example.rs.CustomerService" address="http://localhost:8282/rest"
        depends-on="kc-cxf-endpoint">
        <jaxrs:providers>
            <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider" />
        </jaxrs:providers>
    </jaxrs:server>
  2. META-INF/MANIFEST.MFImport-Package は以下のインポートを含まなければなりません。

META-INF.cxf;version="[2.7,3.3)",
META-INF.cxf.osgi;version="[2.7,3.3)";resolution:=optional,
org.apache.cxf.bus;version="[2.7,3.3)",
org.apache.cxf.bus.spring;version="[2.7,3.3)",
org.apache.cxf.bus.resource;version="[2.7,3.3)",
org.apache.cxf.transport.http;version="[2.7,3.3)",
org.apache.cxf.*;version="[2.7,3.3)",
org.springframework.beans.factory.config,
org.keycloak.*;version="11.0.0"
デフォルトのUndertowエンジンでのApache CXFエンドポイントの保護

いくつかのサービスは、起動時にデプロイされたサーブレットを自動的に提供します。そのようなサービスの1つは、 http://localhost:8181/cxf コンテキストで実行されているCXFサーブレットです。FuseのPax Webは、設定管理を介して既存のコンテキストを変更することをサポートしています。これは、Keycloakでエンドポイントを保護するために使用できます。

アプリケーション内の設定ファイル OSGI-INF/blueprint/blueprint.xml は、以下のようになります。アプリケーションにJAX-RSの customerservice エンドポイント(アプリケーション固有のエンドポイント)を追加することに注意してください。

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs"
           xsi:schemaLocation="
                http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
                http://cxf.apache.org/blueprint/jaxrs http://cxf.apache.org/schemas/blueprint/jaxrs.xsd">

    <!-- JAXRS Application -->
    <bean id="customerBean" class="org.keycloak.example.rs.CxfCustomerService" />

    <jaxrs:server id="cxfJaxrsServer" address="/customerservice">
        <jaxrs:providers>
            <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider" />
        </jaxrs:providers>
        <jaxrs:serviceBeans>
            <ref component-id="customerBean" />
        </jaxrs:serviceBeans>
    </jaxrs:server>
</blueprint>

さらに ${karaf.etc}/org.ops4j.pax.web.context-anyName.cfg file を作成する必要があります。これは、 pax-web-runtime バンドルによって追跡されるファクトリーPID設定として扱われます。このような設定には、標準の web.xml のいくつかのプロパティーに対応する以下のプロパティーを含めることができます。

bundle.symbolicName = org.apache.cxf.cxf-rt-transports-http
context.id = default

context.param.keycloak.config.resolver = org.keycloak.adapters.osgi.HierarchicalPathBasedKeycloakConfigResolver

login.config.authMethod = KEYCLOAK

security.cxf.url = /cxf/customerservice/*
security.cxf.roles = admin, user

設定管理ファイルで利用可能なプロパティーの詳細については、Fuseのドキュメントを参照してください。上記のプロパティーの意味は次のとおりです。

bundle.symbolicNamecontext.id

org.ops4j.pax.web.service.WebContainer 内のバンドルとその配備コンテキストの識別。

context.param.keycloak.config.resolver

古典的なWARの web.xml とまったく同じバンドルへの keycloak.config.resolver コンテキスト・パラメーターの値を提供します。使用可能なリゾルバーについては、設定リゾルバーのセクションで説明しています。

login.config.authMethod

認証方法。 KEYCLOAK でなければなりません。

security.anyName.urlsecurity.anyName.roles

web.xmlsecurity-constraint/web-resource-collection/url-patternsecurity-constraint/auth-constraint/role-name に設定されているように、それぞれ、ロールはカンマとその周囲の空白で区切られます。 anyName 識別子は任意ですが、同じセキュリティー制約の個々のプロパティーに一致する必要があります。

一部のFuseのバージョンには、ロールを ", " (カンマと単一のスペース)で区切らなければならないバグがあります。ロールを区切るためにこの表記法を正確に使用してください。

META-INF/MANIFEST.MFImport-Package は、少なくとも以下のインポートを含んでいなければなりません。

javax.ws.rs;version="[2,3)",
META-INF.cxf;version="[2.7,3.3)",
META-INF.cxf.osgi;version="[2.7,3.3)";resolution:=optional,
org.apache.cxf.transport.http;version="[2.7,3.3)",
org.apache.cxf.*;version="[2.7,3.3)",
com.fasterxml.jackson.jaxrs.json;version="${jackson.version}"
Fuse管理サービスのセキュリティー保護
FuseターミナルへのSSH認証の使用

Keycloakは、主にWebアプリケーションの認証のユースケースを扱います。ただし、他のWebサービスやアプリケーションがKeycloakで保護されている場合は、KeycloakのクレデンシャルでSSHなどのWeb以外の管理サービスを保護するのが最善の方法です。これは、Keycloakへのリモート接続を許可し、リソース・オーナー・パスワード・クレデンシャルに基づいてクレデンシャルを検証するJAASログイン・モジュールを使用して実行できます。

SSH認証を有効にするには、次の手順を実行します。

  1. Keycloakでは、SSH認証に使用されるクライアント(たとえば、 ssh-jmx-admin-client )を作成します。このクライアントでは、 Direct Access Grants EnabledOn に選択されている必要があります。

  2. $FUSE_HOME/etc/org.apache.karaf.shell.cfg ファイルで、次のとおりにこのプロパティーを更新または指定します。

    sshRealm=keycloak
  3. Add the $FUSE_HOME/etc/keycloak-direct-access.json file with content similar to the following (based on your environment and Keycloak client settings):

    {
        "realm": "demo",
        "resource": "ssh-jmx-admin-client",
        "ssl-required" : "external",
        "auth-server-url" : "http://localhost:8080/auth",
        "credentials": {
            "secret": "password"
        }
    }

    このファイルでは、JAASの DirectAccessGrantsLoginModule がSSH認証のために keycloak JAASレルムから使用するクライアント・アプリケーションの設定を指定します。

  4. Fuseを起動し、 keycloak のJAASレルムをインストールしてください。最も簡単な方法は、JAASレルムがあらかじめ定義された keycloak-jaas 機能をインストールすることです。独自の keycloak JAASレルムを使用して、より高いランクでその機能の定義済みレルムを上書きすることができます。詳細については、 JBoss Fuse documentation を参照してください。

    Fuseターミナルで次のコマンドを使用します。

    features:addurl mvn:org.keycloak/keycloak-osgi-features/11.0.0/xml/features
    features:install keycloak-jaas
  5. SSHで admin ユーザーとしてログインするには、ターミナルで次のように入力します。

    ssh -o PubkeyAuthentication=no -p 8101 admin@localhost
  6. パスワードは password でログインしてください。

最近のオペレーティング・システムでは、SSHコマンドの-oオプション -o HostKeyAlgorithms=+ssh-dss を使用する必要があります。これは、最近のSSHクライアントではデフォルトで ssh-dss アルゴリズムの使用は許可されていないためです。しかし、JBoss Fuse 7.4.0では現在デフォルトで使用されています。

ユーザーはすべての操作を実行するために、レルムロール admin を持つ必要があることに注意してください。また、操作のサブセットを実行するためには別のロールが必要となります(たとえば、読み取り専用のKarafコマンドのみを実行するようにユーザーを制限する viewer ロール)。利用可能なロールは、 $FUSE_HOME/etc/org.apache.karaf.shell.cfg または $FUSE_HOME/etc/system.properties で設定されます。

JMX認証の使用

jconsoleまたは別の外部ツールを使用してRMI経由でJMXにリモート接続する場合は、JMX認証が必要になることがあります。そうでなければ、jolokiaエージェントがデフォルトでhawt.ioにインストールされているので、hawt.io/jolokiaを使用する方が良いかもしれません。詳細については、Hawtio Admin Consoleを参照してください。

JMX認証を使用するには、次の手順を実行します。

  1. $FUSE_HOME/etc/org.apache.karaf.management.cfg ファイルで、 jmxRealm プロパティーを次のように変更します。

    jmxRealm=keycloak
  2. 上記のSSHのセクションで説明したように、 keycloak-jaas フィーチャーをインストールし、 $FUSE_HOME/etc/keycloak-direct-access.json ファイルを設定してください。

  3. jconsoleでは、次のようなURLを使用できます。

service:jmx:rmi://localhost:44444/jndi/rmi://localhost:1099/karaf-root

クレデンシャルはadmin/password(利用環境の管理者権限を持つユーザー次第)です。

Hawtio管理コンソールのセキュリティー保護

Keycloakを使用して、Hawtio管理コンソールをセキュリティー保護するには、以下の手順を実行します。

  1. 利用レルムのKeycloak管理コンソールにクライアントを作成します。たとえば、Keycloakの demo レルムでクライアント hawtio-client を作成し、アクセスタイプとして public を指定し、Hawtio: http://localhost:8181/hawtio/* を指すリダイレクトURIを指定します。対応するWeb Origin(この場合は、 http://localhost:8181)を設定します。 hawtio-client クライアント詳細の Scope タブにある account クライアントの view-profile クライアントロールを含むようにクライアント・スコープ・マッピングを設定します。

  2. $FUSE_HOME/etc ディレクトリーに keycloak-hawtio-client.json ファイルを作成します。このファイルは、以下の例のような内容で作成します。Keycloak環境に応じて、 realmresourceauth-server-url の各プロパティーを変更してください。 resource プロパティーは前のステップで作成されたクライアントを指し示さなければなりません。このファイルは、クライアント(Hawtio JavaScriptアプリケーション)側で使用されます。

    {
      "realm" : "demo",
      "clientId" : "hawtio-client",
      "url" : "http://localhost:8080/auth",
      "ssl-required" : "external",
      "public-client" : true
    }
  3. $FUSE_HOME/etc ディレクトリーに keycloak-direct-access.json ファイルを作成します。このファイルは、以下の例のような内容です。Keycloakの環境に応じて realmurl プロパティーを変更してください。このファイルは、JavaScriptクライアントによって使用されます。

    {
      "realm" : "demo",
      "resource" : "ssh-jmx-admin-client",
      "auth-server-url" : "http://localhost:8080/auth",
      "ssl-required" : "external",
      "credentials": {
        "secret": "password"
      }
    }
  4. $FUSE_HOME/etc ディレクトリーに keycloak-hawtio.json ファイルを作成します。このファイルは、以下の例のような内容です。Keycloakの環境に応じて realmauth-server-url プロパティーを変更してください。このファイルは、サーバー(JAASログイン・モジュール)側のアダプターによって使用されます。

    {
      "realm" : "demo",
      "resource" : "jaas",
      "bearer-only" : true,
      "auth-server-url" : "http://localhost:8080/auth",
      "ssl-required" : "external",
      "use-resource-role-mappings": false,
      "principal-attribute": "preferred_username"
    }
  5. JBoss Fuse 7.4.0を開始し、Keycloak featureをインストールします。次に以下をKarafターミナルに入力してください。

    system:property -p hawtio.keycloakEnabled true
    system:property -p hawtio.realm keycloak
    system:property -p hawtio.keycloakClientConfig file://\${karaf.base}/etc/keycloak-hawtio-client.json
    system:property -p hawtio.rolePrincipalClasses org.keycloak.adapters.jaas.RolePrincipal,org.apache.karaf.jaas.boot.principal.RolePrincipal
    restart io.hawt.hawtio-war
  6. http://localhost:8181/hawtio に移動し、Keycloakのレルムからユーザーとしてログインします。

    Hawtioに対して正常に認証するには、ユーザーが適切なレルムロールを持っている必要があることに注意してください。利用可能なロールは、 hawtio.roles$FUSE_HOME/etc/system.properties ファイルで設定されます。

2.1.6. Spring Bootアダプター

Spring Bootアプリケーションをセキュリティー保護するには、Keycloak Spring BootアダプターJARをアプリケーションに追加する必要があります。通常のSpring Bootの設定( application.properties )でいくつかの設定を追加する必要があります。これらの手順について説明します。

アダプターのインストール

Keycloak Spring Bootアダプターは、Spring Bootの自動設定を利用するので、Keycloak Spring Boot Starterをプロジェクトに追加するだけです。

Mavenを使用して追加するには、依存関係に以下を追加します。

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>

アダプターのBOM依存関係も追加して下さい。

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.keycloak.bom</groupId>
      <artifactId>keycloak-adapter-bom</artifactId>
      <version>11.0.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

現在、次の組み込みコンテナーがサポートされており、Starterを使用する場合は特別な依存関係は必要ありません。

  • Tomcat

  • Undertow

  • Jetty

必要なSpring Bootアダプターの設定

このセクションでは、Keycloakを使用するようにSpring Bootアプリケーションを設定する方法について説明します。

keycloak.json ファイルの代わりに、通常のSpring Bootの設定を使って、Spring Boot Keycloakアダプターに対するレルムを設定します。例を以下に示します。

keycloak.realm = demorealm
keycloak.auth-server-url = http://127.0.0.1:8080/auth
keycloak.ssl-required = external
keycloak.resource = demoapp
keycloak.credentials.secret = 11111111-1111-1111-1111-111111111111
keycloak.use-resource-role-mappings = true

keycloak.enabled = false を設定することで、Keycloak Spring Bootアダプターを無効にすることができます(テストなどで)。

Policy Enforcerを設定するには、keycloak.jsonとは異なり、 policy-enforcer の代わりに policy-enforcer-config を使用する必要があります。

また、通常は web.xml に記述するJava EEのセキュリティー設定を指定する必要があります。 Spring Bootアダプターは login-methodKEYCLOAK に設定し、起動時に security-constraints を設定します。 次に設定例を示します。

keycloak.securityConstraints[0].authRoles[0] = admin
keycloak.securityConstraints[0].authRoles[1] = user
keycloak.securityConstraints[0].securityCollections[0].name = insecure stuff
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /insecure

keycloak.securityConstraints[1].authRoles[0] = admin
keycloak.securityConstraints[1].securityCollections[0].name = admin stuff
keycloak.securityConstraints[1].securityCollections[0].patterns[0] = /admin
SpringアプリケーションをWARとしてデプロイする予定の場合は、Spring Bootアダプターを使用せず、使用しているアプリケーション・サーバーまたはサーブレット・コンテナーの専用アダプターを使用してください。Spring Bootには web.xml ファイルも含まれていなければなりません。

2.1.7. Tomcat 7、8、9アダプター

Tomcat 7、8、9にデプロイされたWARアプリケーションを保護するには、Keycloak Tomcat 7アダプターまたはKeycloak TomcatアダプターをTomcatにインストールする必要があります。その後、TomcatにデプロイするWARにもいくつかの設定を行う必要があります。これらの手順について説明します。

アダプターのインストール

アダプターはアプライアンスやwarには含まれていません。各アダプターは、Keycloakのダウンロード・サイトで個別にダウンロードできます。これらは、mavenのアーティファクトとしても利用できます。

アダプターの配布物をTomcatの lib/ ディレクトリーに解凍する必要があります。WEB-INF/libディレクトリー内にアダプターのjarを含めても動作しません!KeycloakアダプターはValveとして実装され、ValveのコードはTomcatのメインのlib/ディレクトリーに存在する必要があります。

Tomcat 7へのインストールは次のとおりです。

$ cd $TOMCAT_HOME/lib
$ unzip keycloak-tomcat7-adapter-dist.zip

Tomcat 8、9へのインストールは次のとおりです。

$ cd $TOMCAT_HOME/lib
$ unzip keycloak-tomcat-adapter-dist.zip
WARごとに必要な設定

このセクションでは、直接WARパッケージ内に設定を追加し、ファイルを編集することで、WARをセキュリティー保護する方法について説明します。

まず、WARパッケージに META-INF/context.xml ファイルを作成します。 これはTomcat固有の設定ファイルであり、Keycloak固有のValveを定義する必要があります。

<Context path="/your-context-path">
    <Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
</Context>

次に、WARの WEB-INF ディレクトリーに keycloak.json アダプター設定ファイルを作成しておく必要があります。

この設定ファイルの形式はJavaアダプターの設定で説明しています。

最後に、URLに対してロールベース制約を指定するために、 login-config と標準のサーブレット・セキュリティーの両方を指定する必要があります。例を次に示します。

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      version="3.0">

        <module-name>customer-portal</module-name>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Customers</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>user</role-name>
        </auth-constraint>
    </security-constraint>

    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>this is ignored currently</realm-name>
    </login-config>

    <security-role>
        <role-name>admin</role-name>
    </security-role>
    <security-role>
        <role-name>user</role-name>
    </security-role>
</web-app>

2.1.8. Jetty 9.xアダプター

Keycloakには、Jetty 9.2.x、Jetty 9.3.x、Jetty 9.4.x用の個別のアダプターがあります。これらをJettyにインストールする必要があります。Jettyにデプロイする各WARには、さらにいくつかの設定を行う必要があります。これらの手順について説明します。

アダプターのインストール

アダプターはアプライアンスやwarには含まれていません。各アダプターは、Keycloakのダウンロード・サイトで個別にダウンロードできます。これらは、mavenのアーティファクトとしても利用できます。

Jetty 9.x用の配布物をJetty 9.xのbaseディレクトリーに解凍する必要があります。WEB-INF/libディレクトリー内にアダプターのjarを含めても動作しません!以下の例では、Jettyベースの名前は your-base です。

$ cd your-base
$ unzip keycloak-jetty93-adapter-dist-2.5.0.Final.zip

次に、Jettyベースの keycloak モジュールを有効にする必要があります。

$ java -jar $JETTY_HOME/start.jar --add-to-startd=keycloak
WARごとに必要な設定

このセクションでは、直接WARパッケージ内に設定を追加し、ファイルを編集することで、WARをセキュリティー保護する方法について説明します。

まず、WARパッケージに WEB-INF/jetty-web.xml ファイルを作成します。これはJetty固有の設定ファイルで、その中にKeycloak固有のAuthenticatorを定義する必要があります。

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
    <Get name="securityHandler">
        <Set name="authenticator">
            <New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
            </New>
        </Set>
    </Get>
</Configure>

次に、WARの WEB-INF ディレクトリーに keycloak.json アダプター設定ファイルを作成しておく必要があります。

この設定ファイルの形式は、Javaアダプターの設定のセクションで説明しています。

Jetty 9.xアダプターは、 keycloak.json ファイルを見つけることができません。下記のように jetty-web.xml ファイル内にすべてのアダプター設定を定義する必要があります。

keycloak.jsonを使用する代わりに、 jetty-web.xml 内にすべてを定義することができます。 json設定が org.keycloak.representations.adapters.config.AdapterConfig クラスとどのようにマッチするかを把握する必要があります。

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
  <Get name="securityHandler">
    <Set name="authenticator">
        <New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
            <Set name="adapterConfig">
                <New class="org.keycloak.representations.adapters.config.AdapterConfig">
                    <Set name="realm">tomcat</Set>
                    <Set name="resource">customer-portal</Set>
                    <Set name="authServerUrl">http://localhost:8081/auth</Set>
                    <Set name="sslRequired">external</Set>
                    <Set name="credentials">
                        <Map>
                            <Entry>
                                <Item>secret</Item>
                                <Item>password</Item>
                            </Entry>
                        </Map>
                    </Set>
                </New>
            </Set>
        </New>
    </Set>
  </Get>
</Configure>

KeycloakでWARをセキュリティー保護するために、WARをオープンする必要はありません。代わりに、yourwar.xmlという名前でwebappsディレクトリーにjetty-web.xmlファイルを作成します。Jettyはそれをピックアップします。このモードでは、keycloak.jsonの設定をxmlファイル内で直接宣言する必要があります。

最後に、URLに対してロールベース制約を指定するために、 login-config と標準のサーブレット・セキュリティーの両方を指定する必要があります。例を次に示します。

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      version="3.0">

        <module-name>customer-portal</module-name>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Customers</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>user</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>

    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>this is ignored currently</realm-name>
    </login-config>

    <security-role>
        <role-name>admin</role-name>
    </security-role>
    <security-role>
        <role-name>user</role-name>
    </security-role>
</web-app>

2.1.9. Spring Securityアダプター

Spring SecurityとKeycloakでアプリケーションを保護するには、このアダプターをプロジェクトのdependencyに追加します。Spring Securityの設定ファイルにいくつか追加のBeanを用意し、パイプラインにKeycloakセキュリティー・フィルターを追加する必要があります。

他のKeycloakアダプターとは異なり、web.xmlにセキュリティーを設定しないでください。ただし、 keycloak.json は依然として必要です。シングル・サインアウトが適切に機能するためには、セッション・リスナーを定義する必要があります。

次のようにセッション・リスナーを定義できます。
  • web.xml内(純粋なSpring Security環境用)

<listener>
     <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
  • Spring Beanとして(Spring Securityアダプターを使用するSpring Boot環境で)

@Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
    return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
アダプターのインストール

MavenのPOMまたはGradleのbuildに、依存するKeycloak Spring Securityアダプターを追加してください。

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-security-adapter</artifactId>
    <version>11.0.0</version>
</dependency>
Spring Securityの設定

Keycloak Spring Securityアダプターは、Spring Securityの柔軟なセキュリティー設定構文を利用します。

Java設定

Keycloakは、 WebSecurityConfigurer を作成するための便利な基本クラスとして、 KeycloakWebSecurityConfigurerAdapter を提供します。この実装では、メソッドのオーバーライドによるカスタマイズが可能です。その使用は必須ではありませんが、セキュリティー・コンテキストの設定が大幅に簡素化されます。

@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
{
    /**
     * Registers the KeycloakAuthenticationProvider with the authentication manager.
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(keycloakAuthenticationProvider());
    }

    /**
     * Defines the session authentication strategy.
     */
    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        super.configure(http);
        http
                .authorizeRequests()
                .antMatchers("/customers*").hasRole("USER")
                .antMatchers("/admin*").hasRole("ADMIN")
                .anyRequest().permitAll();
    }
}

パブリックまたはコンフィデンシャルなアプリケーションの場合は RegisterSessionAuthenticationStrategy の、bearer-onlyアプリケーションの場合は NullAuthenticatedSessionStrategy のタイプのセッション認証ストラテジーBeanを提供する必要があります。

Keycloak経由でログインした後にセッション識別子を変更するため、Spring Securityの SessionFixationProtectionStrategy は現在サポートされていません。セッション識別子が変更された場合、Keycloakは新しいセッション識別子を認識しないため、ユニバーサル・ログアウトは機能しません。

@KeycloakConfiguration アノテーションは、Spring SecurityでKeycloakを統合するために必要なすべてのアノテーションを定義するメタ・データ・アノテーションです。複雑なSpring Securityの設定をする場合は、 @KeycloakConfiguration アノテーションを参照して、独自のカスタム・メタ・アノテーションを作成してください。もしくは、Keycloakアダプター用の特定のSpringアノテーションを使用してください。
XML設定

Spring SecurityのXML名前空間は設定を簡素化しますが、設定のカスタマイズは少し冗長になる可能性があります。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security.xsd">

    <context:component-scan base-package="org.keycloak.adapters.springsecurity" />

    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider ref="keycloakAuthenticationProvider" />
    </security:authentication-manager>

    <bean id="adapterDeploymentContext" class="org.keycloak.adapters.springsecurity.AdapterDeploymentContextFactoryBean">
        <constructor-arg value="/WEB-INF/keycloak.json" />
    </bean>

    <bean id="keycloakAuthenticationEntryPoint" class="org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationEntryPoint">
        <constructor-arg ref="adapterDeploymentContext" />
    </bean>
    <bean id="keycloakAuthenticationProvider" class="org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider" />
    <bean id="keycloakPreAuthActionsFilter" class="org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter" />
    <bean id="keycloakAuthenticationProcessingFilter" class="org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter">
        <constructor-arg name="authenticationManager" ref="authenticationManager" />
    </bean>
    <bean id="keycloakSecurityContextRequestFilter"
          class="org.keycloak.adapters.springsecurity.filter.KeycloakSecurityContextRequestFilter" />

    <bean id="keycloakLogoutHandler" class="org.keycloak.adapters.springsecurity.authentication.KeycloakLogoutHandler">
        <constructor-arg ref="adapterDeploymentContext" />
    </bean>

    <bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <constructor-arg name="logoutSuccessUrl" value="/" />
        <constructor-arg name="handlers">
            <list>
                <ref bean="keycloakLogoutHandler" />
                <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
            </list>
        </constructor-arg>
        <property name="logoutRequestMatcher">
            <bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
                <constructor-arg name="pattern" value="/sso/logout**" />
                <constructor-arg name="httpMethod" value="GET" />
            </bean>
        </property>
    </bean>

    <security:http auto-config="false" entry-point-ref="keycloakAuthenticationEntryPoint">
        <security:custom-filter ref="keycloakPreAuthActionsFilter" before="LOGOUT_FILTER" />
        <security:custom-filter ref="keycloakAuthenticationProcessingFilter" before="FORM_LOGIN_FILTER" />
        <security:custom-filter ref="keycloakSecurityContextRequestFilter" after="FORM_LOGIN_FILTER" />
        <security:intercept-url pattern="/customers**" access="ROLE_USER" />
        <security:intercept-url pattern="/admin**" access="ROLE_ADMIN" />
        <security:custom-filter ref="logoutFilter" position="LOGOUT_FILTER" />
    </security:http>

</beans>
マルチテナンシー

Keycloak Spring Securityアダプターはマルチテナンシーをサポートしています。 AdapterDeploymentContextFactoryBeankeycloak.json へのパスを注入する代わりに、KeycloakConfigResolver インターフェイスの実装を注入することができます。 KeycloakConfigResolver の実装方法の詳細はマルチテナンシーにあります。

セキュリティー・ロールの命名

Spring Securityで、ロールベースの認証を使用する場合、ロール名は ROLE_ で始まる必要があります。たとえば、管理者のロールは単に ADMIN ではなく、 ROLE_ADMIN または同様のものとして、Keycloakで宣言されなければなりません。

org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider クラスはオプションで org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper をサポートしており、Spring Securityによって認識されるロールにKeycloakから来るロールをマップするために使用することができます。 たとえば、 org.springframework.security.core.authority.mapping.SimpleAuthorityMapper を使用して、接頭辞 ROLE_ を挿入し、ロール名を大文字に変換します。 クラスはSpring Security Coreモジュールの一部です。

クライアント・トゥ・クライアント・サポート

クライアント間の通信を簡素化するために、Keycloakはベアラー・トークン認証を処理するSpringの RestTemplate の拡張を提供します。この機能を有効にするには、セキュリティー設定で KeycloakRestTemplate を追加する必要があります。正しく機能するには、プロトタイプとしてスコープを設定する必要があることに注意してください。

Javaによる設定:

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    ...

    @Autowired
    public KeycloakClientRequestFactory keycloakClientRequestFactory;

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public KeycloakRestTemplate keycloakRestTemplate() {
        return new KeycloakRestTemplate(keycloakClientRequestFactory);
    }

    ...
}

XMLによる設定:

<bean id="keycloakRestTemplate" class="org.keycloak.adapters.springsecurity.client.KeycloakRestTemplate" scope="prototype">
    <constructor-arg name="factory" ref="keycloakClientRequestFactory" />
</bean>

アプリケーション・コードは、別のクライアントを呼び出す必要があるときはいつでも、 KeycloakRestTemplate を使うことができます。 次に例を示します。

@Service
public class RemoteProductService implements ProductService {

    @Autowired
    private KeycloakRestTemplate template;

    private String endpoint;

    @Override
    public List<String> getProducts() {
        ResponseEntity<String[]> response = template.getForEntity(endpoint, String[].class);
        return Arrays.asList(response.getBody());
    }
}
Spring Boot統合

Spring BootアダプターとSpring Securityアダプターを組み合わせることができます。

Spring Securityアダプターを使用するために、Keycloak Spring Boot Starterを使用している場合は、Spring Security Starterを追加するだけです。

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
Spring Boot設定の使用

デフォルトでは、Spring Securityアダプターは keycloak.json 設定ファイルを探します。このBeanを追加することで、Spring Bootアダプターが提供する設定を確認できます。

@Bean
public KeycloakConfigResolver keycloakConfigResolver() {
    return new KeycloakSpringBootConfigResolver();
}
Beanの二重登録を避ける

Spring Bootは、フィルターBeanをWebアプリケーション・コンテキストに登録しようとします。そのため、Spring Boot環境でKeycloak Spring Securityアダプターを実行する場合は、Keycloakフィルターが2回登録されないように、セキュリティー設定に FilterRegistrationBean を追加する必要があります。

また、Spring Boot 2.1はデフォルトで spring.main.allow-bean-definition-overriding を無効にしています。これは、もし KeycloakWebSecurityConfigurerAdapter を拡張した Configuration クラスが @ComponentScan によってすでに検出されているBeanを登録しようとすると、 BeanDefinitionOverrideException が発生することを意味します。これについては以下の HttpSessionManager のように、 @ConditionalOnMissingBean アノテーションを使用して登録を上書きすることで避けることができます。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
{
    ...

    @Bean
    public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(
            KeycloakAuthenticationProcessingFilter filter) {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(
            KeycloakPreAuthActionsFilter filter) {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean keycloakAuthenticatedActionsFilterBean(
            KeycloakAuthenticatedActionsFilter filter) {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean keycloakSecurityContextRequestFilterBean(
        KeycloakSecurityContextRequestFilter filter) {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }

    @Bean
    @Override
    @ConditionalOnMissingBean(HttpSessionManager.class)
    protected HttpSessionManager httpSessionManager() {
        return new HttpSessionManager();
    }
    ...
}

2.1.10. Javaサーブレット・フィルター・アダプター

KeycloakアダプターがないプラットフォームにJavaサーブレット・アプリケーションをデプロイする場合は、サーブレット・フィルター・アダプターを使用することを選択します。このアダプターは、他のアダプターとは多少動作が異なります。web.xmlにはセキュリティー制約を定義しません。代わりにKeycloakサーブレット・フィルター・アダプターを使用してフィルター・マッピングを定義して、URLパターンでセキュリティー保護します。

バックチャネル・ログアウトは、標準のアダプターとは少し異なります。HTTPセッションを無効にする代わりに、セッションIDをログアウトしたものとしてマークします。セッションIDに基づいてHTTPセッションを無効にする標準的な方法はありません。
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      version="3.0">

        <module-name>application</module-name>

    <filter>
        <filter-name>Keycloak Filter</filter-name>
        <filter-class>org.keycloak.adapters.servlet.KeycloakOIDCFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>Keycloak Filter</filter-name>
        <url-pattern>/keycloak/*</url-pattern>
        <url-pattern>/protected/*</url-pattern>
    </filter-mapping>
</web-app>

上記のスニペットには2つのURLパターンがあります。 ?/protected/* は保護したいファイルで、/keycloak/* のurl-patternはKeycloakサーバーからのコールバックを処理します。

設定された url-patterns の下にあるいくつかのパスを除外する必要がある場合は、keycloakフィルターが直ちにフィルター・チェーンに委譲すべきパス・パターンを記述する正規表現を設定するために、フィルターのinit-param keycloak.config.skipPattern を使用できます。デフォルトでは、skipPatternは設定されていません。

パターンは コンテキスト・パス なしで requestURI と照合されます。コンテキスト・パス /myapp が与えられると、 /myapp/index.html のリクエストは、スキップ・パターンと /index.html で照合されます。

<init-param>
    <param-name>keycloak.config.skipPattern</param-name>
    <param-value>^/(path1|path2|path3).*</param-value>
</init-param>

Keycloak管理コンソールでクライアントのURLを設定する必要があります。Admin URLは、フィルターのurl-patternで保護されたセクションを指します。

Admin URLは、バックチャネル・ログアウトなどの操作を行うために、管Admin URLへのコールバックを行います。したがって、この例のAdmin URLは http[s]://hostname/{context-root}/keycloak でなければなりません。

Keycloakフィルターは、コンテキスト・パラメーターの代わりにフィルター初期化パラメーターとして定義する必要がある以外は、他のアダプターと同じ設定パラメーターを持ちます。

To use this filter, include this maven artifact in your WAR poms:

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-servlet-filter-adapter</artifactId>
    <version>11.0.0</version>
</dependency>
OSGiでの使用

サーブレット・フィルター・アダプターは、OSGiバンドルとしてパッケージされているため、HTTP ServiceとHTTP Whiteboardを使用する汎用OSGi環境(R6以上)で使用できます。

インストール

アダプターとその依存関係はMavenのアーティファクトとして配布されるため、Maven Centralにアクセスするためにインターネット接続を使用するか、ローカルのMavenリポジトリーにアーティファクトをキャッシュする必要があります。

Apache Karafを使用している場合は、Keycloakフィーチャー・リポジトリーからフィーチャーをインストールするだけです。

karaf@root()> feature:repo-add mvn:org.keycloak/keycloak-osgi-features/11.0.0/xml/features
karaf@root()> feature:install keycloak-servlet-filter-adapter

その他のOSGiランタイムについては、ランタイム・ドキュメントでアダプター・バンドルとその依存関係をインストールする方法を参照してください。

OSGiプラットフォームがPax WebとApache Karafである場合は、代わりにJBoss Fuse 6アダプターまたはJBoss Fuse 7アダプターを使用することを検討してください。
設定

まず、アダプターをOSGi HTTP Serviceでサーブレット・フィルターとして登録する必要があります。これを行う最も一般的な方法は、プログラム的な方法(例えば、バンドル・アクティベーターを介して)と宣言的な方法(OSGiアノテーションを使用して)です。フィルターを動的に登録および登録解除するプロセスが簡素化されるため、後者を使用することをお勧めします。

package mypackage;

import javax.servlet.Filter;
import org.keycloak.adapters.servlet.KeycloakOIDCFilter;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;

@Component(
    immediate = true,
    service = Filter.class,
    property = {
        KeycloakOIDCFilter.CONFIG_FILE_PARAM + "=" + "keycloak.json",
        HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN + "=" +"/*",
        HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT + "=" + "(osgi.http.whiteboard.context.name=mycontext)"
    }
)
public class KeycloakFilter extends KeycloakOIDCFilter {
  //
}

上記のスニペットは、OSGi宣言型サービスの仕様を使用して、 javax.servlet.Filter クラスの下でOSGIサービスとしてフィルターを公開します。クラスがOSGiサービス・レジストリーにパブリッシュされると、OSGi HTTP Serviceの実装によってピックアップされ、指定されたサーブレット・コンテキストへのリクエストをフィルタリングするために使用されます。これにより、サーブレットのコンテキストパス+フィルターパスに一致するすべてのリクエストに対して、Keycloakアダプターがトリガーされます。

コンポーネントはOSGi Configuration Admin Serviceの制御下に置かれるため、そのプロパティーは動的に設定できます。これを行うには、OSGiランタイムの標準の設定場所に mypackage.KeycloakFilter.cfg ファイルを作成するか、

keycloak.config.file = /path/to/keycloak.json
osgi.http.whiteboard.filter.pattern = /secure/*

または、ランタイムが可能な場合は、対話型コンソールを使用してください。

karaf@root()> config:edit mypackage.KeycloakFilter
karaf@root()> config:property-set keycloak.config.file '${karaf.etc}/keycloak.json'
karaf@root()> config:update

より多くの制御が必要な場合、たとえばマルチテナンシーを実装するカスタム KeycloakConfigResolver を提供する場合は、次のようにフィルターをプログラムで登録することができます。

public class Activator implements BundleActivator {

  private ServiceRegistration registration;

  public void start(BundleContext context) throws Exception {
    Hashtable props = new Hashtable();
    props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN, "/secure/*");
    props.put(KeycloakOIDCFilter.CONFIG_RESOLVER_PARAM, new MyConfigResolver());

    this.registration = context.registerService(Filter.class.getName(), new KeycloakOIDCFilter(), props);
  }

  public void stop(BundleContext context) throws Exception {
    this.registration.unregister();
  }
}

プログラムによる登録の詳細については、 Apache Felix HTTP Service を参照してください。

2.1.11. JAASプラグイン

ほとんどのアプリケーションでJAASを使用する必要はありません。特にHTTPベースのアプリケーションの場合は、JAASを使用する必要はありません。 しかし、一部のアプリケーションやシステムは、依然として純粋なレガシーJAASソリューションに依存している場合があります。Keycloakは、このような状況に役立つ2つのログイン・モジュールを提供します。

提供されるログイン・モジュールは次のとおりです。

org.keycloak.adapters.jaas.DirectAccessGrantsLoginModule

このログイン・モジュールは、Keycloakのユーザー名/パスワードで認証することができます。リソース・オーナー・パスワード・クレデンシャルのフローを使用して、提供されたユーザー名/パスワードが有効かどうかを検証します。JAASに依存する必要があり、Keycloakを使用したい非Webベースのシステムにとっては有益ですが、Web以外の性質のため、標準のブラウザー・ベースのフローを使用することはできません。このようなアプリケーションとして挙げられるのは、メッセージングやSSHです。

org.keycloak.adapters.jaas.BearerTokenLoginModule

このログイン・モジュールでは、パスワードとして CallbackHandler を介して渡されたKeycloakのアクセストークンで認証できます。たとえば、標準ベースの認証フローからKeycloakのアクセストークンを取得し、WebアプリケーションがJAASに依存する外部の非Webベースのシステムと対話する必要がある場合などに便利です。たとえば、メッセージング・システムです。

どちらのモジュールも次の設定プロパティーを使用します。

keycloak-config-file

keycloak.json 設定ファイルの場所。設定ファイルは、ファイルシステム上またはクラスパス上に置くことができます。クラスパス上にある場合は、その場所の前に classpath: を付ける必要があります(たとえば classpath:/path/keycloak.json )。これは 必須 です。

role-principal-class

JAAS Subjectに添付されているロール・プリンシパルの代替クラスを設定します。デフォルト値は org.keycloak.adapters.jaas.RolePrincipal です。注意:クラスには、単一の String 引数を持つコンストラクタが必要です。

scope

このオプションは DirectAccessGrantsLoginModule にのみ適用されます。指定された値は、リソース・オーナー・パスワード・クレデンシャル・グラント・リクエストのOAuth2 scope パラメーターとして使用されます。

2.1.12. CLI / デスクトップ・アプリケーション

Keycloakは、システム・ブラウザーを介して認証ステップを実行することによって、 KeycloakInstalled アダプターを介したデスクトップ(たとえば、Swing、JavaFX)またはCLIアプリケーションのセキュリティー保護をサポートします。

KeycloakInstalled アダプターは、 desktopmanual のバリアントをサポートしています。デスクトップ・バリアントは、システム・クレデンシャルを収集するためにシステム・ブラウザーを使用します。手動バリアントは、ユーザー・クレデンシャルを STDIN から読み込みます。

どのように動くか

desktop バリアントを使用してユーザーを認証するために、 KeycloakInstalled アダプターはデスクトップ・ブラウザーのウィンドウを開きます。ここで KeycloakInstalled オブジェクトに対して loginDesktop() メソッドが呼び出されると、ユーザーは通常のKeycloakログインページを使用してログインします。

ログイン・ページのURLは、アダプターによって起動される localhost の空きエフェメラル・ポートをリッスンするローカル ServerSocket を指すリダイレクト・パラメーターでオープンされます。

ログイン成功後、 KeycloakInstalled は受信したHTTPリクエストから認可コードを受け取り、認可コード・フローを実行します。トークンからコードへの交換が完了すると、 ServerSocket はシャットダウンされます。

ユーザーがすでにKeycloakのアクティブなセッションを持っている場合、ログイン・フォームは表示されませんが、コードからトークンへの交換は継続され、スムーズなWebベースのSSO体験が可能になります。

クライアントは、最終的にバックエンド・サービスを呼び出すために使用できるトークン(access_token、refresh_token、id_token)を受け取ります。

KeycloakInstalled アダプターは失効したトークンの更新をサポートします。

アダプターのインストール
<dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-installed-adapter</artifactId>
        <version>11.0.0</version>
</dependency>
クライアントの設定

アプリケーションは、 Standard Flow Enabledpublic なOpenID Connectクライアントであり、許可される Valid Redirect URI として http://localhost が設定される必要があります。

KeycloakInstalled アダプターは、 OIDC プロトコルでのコードからトークンへの交換中に追加の保護を提供する PKCE [RFC 7636] メカニズムをサポートします。PKCEは、アダプター設定の "enable-pkce": true の設定で有効にできます。コード・インジェクションおよびコードリプレイ攻撃を回避するために、PKCEを有効にすることを強くお勧めします。
使い方

KeycloakInstalled アダプターはクラスパス上の META-INF/keycloak.json から設定を読み込みます。カスタム設定は、 KeycloakInstalled コンストラクタを介して、 InputStream または KeycloakDeployment で提供することができます。

以下の例では、 desktop-app のクライアント設定は keycloak.json を使います。

{
  "realm": "desktop-app-auth",
  "auth-server-url": "http://localhost:8081/auth",
  "ssl-required": "external",
  "resource": "desktop-app",
  "public-client": true,
  "use-resource-role-mappings": true,
  "enable-pkce": true
}

次のスケッチは、 KeycloakInstalled アダプターとの動作を示しています。

// reads the configuration from classpath: META-INF/keycloak.json
KeycloakInstalled keycloak = new KeycloakInstalled();

// opens desktop browser
keycloak.loginDesktop();

AccessToken token = keycloak.getToken();
// use token to send backend request

// ensure token is valid for at least 30 seconds
long minValidity = 30L;
String tokenString = keycloak.getTokenString(minValidity, TimeUnit.SECONDS);


 // when you want to logout the user.
keycloak.logout();
KeycloakInstalled クラスは loginResponseWriter 属性と logoutResponseWriter 属性を介したログイン/ログアウト・リクエストによって、返されたHTTPレスポンスのカスタマイズをサポートしています。

以下に、上記の設定例を示します。

import java.util.Locale;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.keycloak.adapters.installed.KeycloakInstalled;
import org.keycloak.representations.AccessToken;

public class DesktopApp {

        public static void main(String[] args) throws Exception {

                KeycloakInstalled keycloak = new KeycloakInstalled();
                keycloak.setLocale(Locale.ENGLISH);
                keycloak.loginDesktop();

                AccessToken token = keycloak.getToken();
                Executors.newSingleThreadExecutor().submit(() -> {

                        System.out.println("Logged in...");
                        System.out.println("Token: " + token.getSubject());
                        System.out.println("Username: " + token.getPreferredUsername());
                        try {
                                System.out.println("AccessToken: " + keycloak.getTokenString());
                        } catch (Exception ex) {
                                ex.printStackTrace();
                        }

                        int timeoutSeconds = 20;
                        System.out.printf("Logging out in...%d Seconds%n", timeoutSeconds);
                        try {
                                TimeUnit.SECONDS.sleep(timeoutSeconds);
                        } catch (Exception e) {
                                e.printStackTrace();
                        }

                        try {
                                keycloak.logout();
                        } catch (Exception e) {
                                e.printStackTrace();
                        }

                        System.out.println("Exiting...");
                        System.exit(0);
                });
        }
}

2.1.13. セキュリティー・コンテキスト

KeycloakSecurityContext インターフェイスはトークンに直接アクセスする必要がある場合に利用できます。これは、トークンから追加の詳細情報(ユーザー・プロファイル情報など)を取得する場合や、Keycloakによって保護されているRESTfulサービスを呼び出す場合に便利です。

サーブレット環境では、セキュアな呼び出しで HttpServletRequest の属性として以下のようにセキュリティー・コンテキストを利用できます。

httpServletRequest
    .getAttribute(KeycloakSecurityContext.class.getName());

または、セキュアでないリクエストでは以下のように HttpSession からセキュリティー・コンテキストを利用できます。

httpServletRequest.getSession()
    .getAttribute(KeycloakSecurityContext.class.getName());

2.1.14. エラー処理

Keycloakには、サーブレットベースのクライアント・アダプター用のエラー処理機能があります。認証でエラーが発生すると、Keycloakは HttpServletResponse.sendError() を呼び出します。 web.xml ファイル内にerror-pageを設定してエラーを処理することができます。Keycloakは400、401、403、500のエラーをスローできます。

<error-page>
    <error-code>403</error-code>
    <location>/ErrorHandler</location>
</error-page>

Keycloakはまた、取得可能な HttpServletRequest 属性を設定します。属性名は、 org.keycloak.adapters.spi.AuthenticationError です。これは org.keycloak.adapters.OIDCAuthenticationError にキャストする必要があります。

例:

import org.keycloak.adapters.OIDCAuthenticationError;
import org.keycloak.adapters.OIDCAuthenticationError.Reason;
...

OIDCAuthenticationError error = (OIDCAuthenticationError) httpServletRequest
    .getAttribute('org.keycloak.adapters.spi.AuthenticationError');

Reason reason = error.getReason();
System.out.println(reason.name());

2.1.15. ログアウト

Webアプリケーションからログアウトする方法は複数あります。Java EEサーブレット・コンテナーの場合、 HttpServletRequest.logout() を呼び出すことができます。他のブラウザー・アプリケーションの場合、ブラウザーにSSOセッションがあれば、 http://auth-server/auth/realms/{realm-name}/protocol/openid-connect/logout?redirect_uri=encodedRedirectUri にリダイレクトすることでログアウトできます。

HttpServletRequest.logout() を使用すると、アダプターはKeycloakサーバーに対して、バックチャネルでリフレッシュトークンを渡すPOSTリクエストを送信します。保護されていないページ(有効なトークンをチェックしていないページ)からメソッドが実行された場合、リフレッシュトークンは使用できない可能性があり、その場合はアダプターがPOSTリクエストの送信をスキップします。このため、現在のトークンが常に考慮され、必要に応じてKeycloakサーバーとの対話が実行されるように、保護されたページを使用して HttpServletRequest.logout() を実行することを推奨します。

ログアウト・プロセスの一部として外部アイデンティティー・プロバイダーからログアウトしないようにするには、パラメーター initiating_idp を指定します。値は当該のアイデンティティー・プロバイダーのアイデンティティー(エイリアス)です。これは、外部アイデンティティー・プロバイダーによって開始されたシングルログアウトの一部として、ログアウト・エンドポイントが呼び出された場合に便利です。

2.1.16. パラメーター・フォワーディング

Keycloakの初期認可エンドポイント・リクエストは、さまざまなパラメーターをサポートしています。ほとんどのパラメータは OIDC仕様 に記述されています。一部のパラメーターは、アダプターの設定に基づいて、アダプターにより自動的に追加されます。ただし、呼び出しごとに追加できるパラメーターもいくつかあります。保護されたアプリケーションURIにアクセスすると、特定のパラメーターはKeycloak認可エンドポイントにフォワードされます。

たとえば、オフライントークンを要求する場合、以下のように scope パラメーターを使用して保護されたアプリケーションのURIにアクセスできます。

http://myappserver/mysecuredapp?scope=offline_access

パラメーター scope=offline_access が自動的にKeycloak認可エンドポイントにフォワードされます。

サポートされるパラメーターは次のとおりです。

  • scope - スペース区切りのスコープリストを使用します。スペースで区切られたリストは通常、特定のクライアントで定義されたクライアントスコープを参照します。スコープ openid は、アダプターによって常にスコープのリストに追加されることに注意してください。たとえば、スコープ・オプション address phone を入力すると、Keycloakへのリクエストにスコープ・パラメーター scope=openid address phone が含まれます。

  • prompt - Keycloakは以下の設定をサポートしています: login - SSOは無視され、ユーザーがすでに認証されていてもKeycloakログインページが常に表示されます consent - Consent Required のクライアントにのみ適用されます。これを使用すると、ユーザーが以前にこのクライアントに同意したとしても、常に同意ページが表示されます。 ** none - ログインページは表示されません。代わりにユーザーはアプリケーションにリダイレクトされ、ユーザーがまだ認証されていない場合はエラーが発生します。この設定により、アプリケーション側でフィルター/インターセプターを作成し、ユーザーにカスタム・エラーページを表示することができます。詳細については、仕様を参照してください。

  • max_age - ユーザーがすでに認証されている場合にのみ使用されます。ユーザーが認証されたときから測定された、認証が維持される最大許容時間を指定します。ユーザーが認証されてから maxAge が経過すると、SSOは無視され、再認証が必要になります。

  • login_hint - ログイン・フォームのユーザー名/電子メール・フィールドを事前入力するために使用されます。

  • kc_idp_hint - Keycloakにログインページの表示をスキップし、代わりに指定されたアイデンティティー・プロバイダーに自動的にリダイレクトするように指示するために使用されます。詳細は、アイデンティティー・プロバイダーのドキュメントを参照してください。

ほとんどのパラメーターは OIDC仕様 に記載されています。唯一の例外はパラメーター kc_idp_hint です。これはKeycloak固有で、自動的に使用するアイデンティティー・プロバイダーの名前を含んでいます。詳細は Server Administration Guideアイデンティティー・ブローカリング のセクションを参照してください。

アプリケーションで既に認証されている場合、添付されたパラメーターを使用してURLを開いても、アダプターはKeycloakにリダイレクトしません。たとえば、アプリケーションmysecredappにすでに認証されている場合、 http://myappserver/mysecuredapp?prompt=login を開いても、自動的にKeycloakログインページにリダイレクトされません。この挙動は将来変更される可能性があります。

2.1.17. クライアント認証

コンフィデンシャルOIDCクライアントがバックチャネル・リクエストを送信する必要がある場合(たとえば、トークンのコードを交換したり、トークンをリフレッシュするような場合)、Keycloakサーバーに対して認証する必要があります。デフォルトでは、クライアントIDとクライアント・シークレット、署名付きJWTによるクライアント認証、またはクライアント・シークレットを使用した署名付きJWTによるクライアント認証の3つの方法でクライアントを認証します。

クライアントIDとクライアント・シークレット

これは、OAuth2の仕様で説明されている伝統的な方法です。クライアントにはシークレットがあり、アダプター(アプリケーション)とKeycloakサーバーの両方に知られている必要があります。Keycloak管理コンソールで特定のクライアントのシークレットを生成し、このシークレットをアプリケーション側の keycloak.json ファイルに以下のように貼り付けます。

"credentials": {
    "secret": "19666a4f-32dd-4049-b082-684c74115f28"
}
署名付きJWTによるクライアント認証

これは RFC7523 の仕様に基づいています。以下の方法で動作します。

  • クライアントには秘密鍵と証明書が必要です。Keycloakの場合、これは伝統的な keystore ファイルから利用できます。これはクライアント・アプリケーションのクラスパスかファイルシステムのどこかで利用できます。

  • クライアント・アプリケーションが開始されると、 http://myhost.com/myapp/k_jwks のようなURLを使った JWKS 形式の公開鍵のダウンロードが許可されます。 http://myhost.com/myapp はクライアント・アプリケーションのベースURLであることを前提としています。このURLはKeycloakにより使用されます(下記参照)。

  • 認証中に、クライアントはJWTトークンを生成し、その秘密鍵で署名し、 client_assertion パラメーターとともに特定のバックチャネル・リクエスト(たとえば、コードからトークンへの交換リクエスト)をKeycloakに送信します。

  • Keycloakは、JWTの署名を検証できるように、クライアントの公開鍵または証明書を持っていなければなりません。Keycloakでは、クライアントのクライアント・クレデンシャルを設定する必要があります。まず、管理コンソールの Credentials タブでクライアントを認証する方法として、 Signed JWT を選択する必要があります。そして、次のどちらかを選択することができます。 Keycloakがクライアントの公開鍵をダウンロードできるJWKS URLを設定します。これは http://myhost.com/myapp/k_jwks のようなURLです(詳細は上記を参照)。クライアントはいつでもキーをローテーションさせることができるので、このオプションは最も柔軟です。Keycloakは、設定を変更することなく、必要なときに常に新しいキーをダウンロードします。より正確には、Keycloakは、未知の kid (Key ID)で署名されたトークンを見ると、新しい鍵をダウンロードします。 クライアントの公開鍵または証明書を、PEM形式、JWK形式、またはキーストアからアップロードします。このオプションを使用すると、公開鍵はハードコードされるので、クライアントが新しい鍵ペアを生成するときに変更する必要があります。独自のキーストアがない場合は、Keycloak管理コンソールから独自のキーストアを生成することもできます。Keycloak管理コンソールの設定方法の詳細については、 Server Administration Guide を参照してください。

アダプター側でセットアップするには、 keycloak.json ファイルに次のようなものが必要です。

"credentials": {
  "jwt": {
    "client-keystore-file": "classpath:keystore-client.jks",
    "client-keystore-type": "JKS",
    "client-keystore-password": "storepass",
    "client-key-password": "keypass",
    "client-key-alias": "clientkey",
    "token-expiration": 10
  }
}

この設定では、キーストア・ファイル keystore-client.jks がWARのクラスパス上で利用可能でなければなりません。 classpath: というプレフィックスを使用しない場合は、クライアント・アプリケーションが実行されているファイルシステム上のファイルを指すことができます。

インスピレーションのために、 product-portal アプリケーションのデモのサンプル配布物を見てみることができます。

クライアント・シークレットを使用した署名付きJWTによるクライアント認証

これは、署名付きJWTによるクライアント認証と同じですが、秘密鍵と証明書の代わりにクライアント・シークレットを使用する点が異なります。

クライアントにはシークレットがあり、アダプター(アプリケーション)とKeycloakサーバーの両方に知られている必要があります。管理コンソールの Credentials タブでクライアントを認証する方法として Signed JWT with Client Secret を選択し、このシークレットをアプリケーション側の keycloak.json ファイルに以下のように貼り付けます。

"credentials": {
  "secret-jwt": {
    "secret": "19666a4f-32dd-4049-b082-684c74115f28",
    "algorithm": "HS512"
  }
}

"algorithm"フィールドには、クライアント・シークレットを使用した署名付きJWTのアルゴリズムを指定します。HS256、HS384、HS512のいずれかの値である必要があります。詳細については、 JSON Web Algorithms(JWA) を参照してください。

この"algorithm"フィールドはオプションであり、"algorithm"フィールドが keycloak.json ファイルに存在しない場合はHS256が自動的に適用されます。

独自のクライアント認証方式の追加

独自のクライアント認証方式を追加することもできます。クライアントサイドとサーバーサイドの両方のプロバイダーを実装する必要があります。詳細は Server Developer GuideAuthentication SPI のセクションを参照してください。

2.1.18. マルチテナンシー

マルチテナンシーとは、単一のターゲット・アプリケーション(WAR)が複数のKeycloakのレルムで保護されることを意味します。レルムは、同じKeycloakインスタンスまたは異なるインスタンスに配置できます。

実際には、これはアプリケーションが複数の keycloak.json アダプター設定ファイルを持つ必要があることを意味します。

さまざまなコンテキストパスに異なるアダプター設定ファイルをデプロイして、WARの複数のインスタンスを作成することができます。しかし、これは不便かもしれませんし、コンテキストパス以外のものに基づいてレルムを選択するようにできます。

Keycloakはカスタムの設定リゾルバーを持つことができるので、各リクエストにどのアダプター設定が使われるかを選ぶことができます。

まずこれを達成するためには、 org.keycloak.adapters.KeycloakConfigResolver の実装を作成する必要があります。たとえば、以下のようになります。

package example;

import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;

public class PathBasedKeycloakConfigResolver implements KeycloakConfigResolver {

    @Override
    public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
        if (path.startsWith("alternative")) {
            KeycloakDeployment deployment = cache.get(realm);
            if (null == deployment) {
                InputStream is = getClass().getResourceAsStream("/tenant1-keycloak.json");
                return KeycloakDeploymentBuilder.build(is);
            }
        } else {
            InputStream is = getClass().getResourceAsStream("/default-keycloak.json");
            return KeycloakDeploymentBuilder.build(is);
        }
    }

}

また、以下のように web.xmlkeycloak.config.resolver のcontext-paramでどの KeycloakConfigResolver 実装を使うかを設定する必要もあります。

<web-app>
    ...
    <context-param>
        <param-name>keycloak.config.resolver</param-name>
        <param-value>example.PathBasedKeycloakConfigResolver</param-value>
    </context-param>
</web-app>

2.1.19. アプリケーション・クラスタリング

この章では、JBoss EAP、WildFly、またはJBoss ASにデプロイされてクラスター化されたアプリケーションのサポートについて解説します。

アプリケーションが次のような場合には、いくつかのオプションがあります。

  • ステートレスまたはステートフル

  • 分散可能(HTTPセッション・レプリケーション)または分散不可能

  • ロードバランサーによって提供されるスティッキー・セッションに依存する

  • Keycloakと同じドメインでホストされている

クラスタリングを扱うことは、通常のアプリケーションほど簡単ではありません。ブラウザーとサーバーサイド・アプリケーションの両方がKeycloakにリクエストを送信することになるため、ロードバランサーでスティッキー・セッションを有効にするほど簡単ではありません。

ステートレス・トークン・ストア

デフォルトでは、Keycloakによって保護されたWebアプリケーションは、HTTPセッションを使用してセキュリティー・コンテキストを保存します。つまり、スティッキー・セッションを有効にするか、HTTPセッションをレプリケーションする必要があります。

HTTPセッションにセキュリティー・コンテキストを保存する代わりに、これをCookieに保存するようにアダプターを設定することもできます。これは、アプリケーションをステートレスにする場合や、セキュリティー・コンテキストをHTTPセッションに保存したくない場合に便利です。

セキュリティー・コンテキストを保存するためにCookieストアを使用するには、アプリケーションの WEB-INF/keycloak.json を編集して次の行を追加します。

"token-store": "cookie"
token-store のデフォルト値は session です。これにより、セキュリティー・コンテキストがHTTPセッションに保存されます。

Cookieストアを使用する際の制限の1つは、すべてのHTTPリクエストに対して、セキュリティー・コンテキスト全体がCookieに渡されることです。これはパフォーマンスに影響する可能性があります。

もう一つの小さな制限は、シングルサイン・アウトのサポートが限られていることです。アダプターが KEYCLOAK_ADAPTER_STATE クッキーを削除するので、アプリケーション自体からサーブレット・ログアウト( HttpServletRequest.logout )を開始すると、問題なく動作します。 ただし、別のアプリケーションからの初期化されたバックチャネル・ログアウトは、KeycloakによってCookieストアを使用するアプリケーションに伝播されません。したがって、アクセストークンのタイムアウトに短い値(たとえば1分)を使用することをお勧めします。

一部のロードバランサーは、スティッキー・セッションCookieの名前や内容の設定を許可しません(Amazon ALBなど)。これらのために、 shouldAttachRoute オプションを false に設定することが推奨されます。
相対URIの最適化

Keycloakとアプリケーションが同じドメイン(リバース・プロキシーまたはロードバランサーを介して)でホストされているデプロイメントのシナリオでは、クライアント設定で相対URIオプションを使用すると便利です。

相対URIの場合、URIはKeycloakにアクセスするために使用されるURLに関連して解決されます。

たとえば、アプリケーションへのURLが https://acme.org/myapp で、KeycloakへのURLが https://acme.org/auth である場合、 https://acme.org/myapp の代わりにリダイレクトURIの /myapp を使うことができます。

管理URLの設定

特定のクライアント用の管理URLは、Keycloak管理コンソールで設定できます。これは、Keycloakサーバーによって、ユーザーのログアウトや取消しポリシーのプッシュなどのさまざまなタスクのバックエンド・リクエストをアプリケーションに送信するために使用されます。

たとえば、バックチャネル・ログアウトの仕組みは次のとおりです。

  1. ユーザーが1つのアプリケーションからログアウト・リクエストを送信します

  2. アプリケーションはKeycloakにログアウト・リクエストを送信します

  3. Keycloakサーバーはユーザー・セッションを無効にします

  4. Keycloakサーバーは、セッションに関連付けられた管理URLでバックチャネル・リクエストをアプリケーションに送信します

  5. アプリケーションがログアウト・リクエストを受信すると、対応するHTTPセッションが無効になります

管理URLに ${application.session.host} が含まれていると、HTTPセッションに関連付けられたノードのURLに置き換えられます。

アプリケーション・ノードの登録

前のセクションでは、Keycloakが特定のHTTPセッションに関連付けられたノードにログアウト・リクエストを送信する方法について説明しました。しかし、管理者が管理タスクを登録されている全てのクラスターノードに伝播させたい場合があります。たとえば、新しいnot beforeポリシーをアプリケーションにプッシュする、またはアプリケーションからすべてのユーザーをログアウトする場合などです。

この場合、Keycloakはすべてのアプリケーション・クラスター・ノードを認識する必要があります。これにより、全てのノードにイベントを送信できます。これを達成するために、オート・ディスカバリーのメカニズムをサポートしています。

  1. 新しいアプリケーション・ノードがクラスターに参加すると、Keycloakサーバーに登録リクエストを送信します。

  2. 定期的に設定された間隔で、設定がKeycloakに送信されます

  3. Keycloakサーバーが指定されたタイムアウト時間内に再登録リクエストを受信しなかった場合、Keycloakサーバーは自動的に特定のノードの登録を解除します

  4. また、登録解除リクエストを送信するときに、ノードはKeycloakで登録抹消されます。これは、通常、ノードのシャットダウンまたはアプリケーションのアンデプロイメント中です。アンデプロイメント・リスナーが呼び出されない場合、強制シャットダウンで正しく動作しないことがあります。その結果、自動登録解除が必要になります

スタートアップ登録の送信と定期的な再登録は、クラスター化された一部のアプリケーションでのみ必要なので、デフォルトでは無効になっています。

この機能を有効にするには、アプリケーションの WEB-INF/keycloak.json ファイルを編集し、次の行を追加します。

"register-node-at-startup": true,
"register-node-period": 600,

これは、アダプターが起動時に登録リクエストを送信し、10分ごとに再登録することを意味します。

Keycloak管理コンソールでは、最大ノード再登録タイムアウトを指定できます(アダプター設定から register-node-period より大きい値にする必要があります)。管理コンソールから、クラスター・ノードを手動で追加したり、削除することもできます。自動登録機能を使用したくない場合や、自動登録解除機能を使用しない場合、失効したアプリケーション・ノードを削除する場合に便利です 。

各リクエストのトークンのリフレッシュ

デフォルトでは、アプリケーション・アダプターは期限切れになったときにのみ、アクセストークンを更新します。ただし、リクエストごとにトークンをリフレッシュするようにアダプターを設定することもできます。これは、アプリケーションがKeycloakサーバーにさらに多くのリクエストを送信するため、パフォーマンスに影響を与える可能性があります。

この機能を有効にするには、アプリケーションの WEB-INF/keycloak.json ファイルを編集し、次の行を追加します。

"always-refresh-token": true
これはパフォーマンスに重大な影響を与える可能性があります。ログアウトとnot beforeポリシーを伝播するために、バックチャネル・メッセージに頼ることができない場合にのみ、この機能を有効にしてください。考慮すべきことは、デフォルトではアクセストークンの有効期限が短いので、ログアウトが伝播されなくてもログアウトから数分以内にトークンが期限切れになることです。

2.2. JavaScriptアダプター

Keycloakには、HTML5/JavaScriptアプリケーションをセキュリティー保護するために使用可能なクライアント・サイドのJavaScriptライブラリーが付属しています。JavaScriptアダプターには、Cordovaアプリケーションのサポートが組み込まれています。

ライブラリーはKeycloakサーバーの /auth/js/keycloak.js から直接取得することができ、ZIPアーカイブとしても配布されています。

Keycloakサーバーをアップグレードする際に自動的にJavaScriptアダプターが更新されるため、ベスト・プラクティスはKeycloakサーバーからJavaScriptアダプターを直接ロードすることです。そうではなくアダプターをWebアプリケーションにコピーする場合は、サーバーをアップグレードした後に、アダプターもアップグレードするようにしてください。

クライアント・サイドのアプリケーションを使用する際に注意すべき重要な点の1つは、クライアント・サイドのアプリケーションにクライアント・クレデンシャルを安全に保存する方法がないため、クライアントをパブリック・クライアントにする必要があることです。これにより、クライアント用に設定したリダイレクトURIが正しいか、できるだけ具体的であるかを確認することが非常に重要になります。

JavaScriptアダプターを使用するには、まずKeycloak管理コンソールでアプリケーション用のクライアントを作成する必要があります。 Access Typepublic が選択されていることを確認してください。

また、 Valid Redirect URIsWeb Origins を設定する必要があります。できるだけ具体的にする理由は、そうしなければセキュリティー上の脆弱性が生じる可能性があるためです。

クライアントが作成されたら Installation タブをクリックし、 Format Option の中から Keycloak OIDC JSON を選択して、 Download をクリックします。ダウンロードした keycloak.json ファイルは、HTMLページと同じ場所にあるWebサーバー上にホストされるべきです。

または、設定ファイルではなく、アダプターを手動で設定することもできます。

次の例は、JavaScriptアダプターを初期化する方法を示しています。

<html>
<head>
    <script src="keycloak.js"></script>
    <script>
        function initKeycloak() {
            var keycloak = new Keycloak();
            keycloak.init().then(function(authenticated) {
                alert(authenticated ? 'authenticated' : 'not authenticated');
            }).catch(function() {
                alert('failed to initialize');
            });
        }
    </script>
</head>
<body onload="initKeycloak()">
    <!-- your page content goes here -->
</body>
</html>

keycloak.json ファイルが別の場所にある場合、以下のように指定することができます。

var keycloak = new Keycloak('http://localhost:8080/myapp/keycloak.json');

または、以下のように必要な設定でJavaScriptオブジェクトを渡すこともできます。

var keycloak = new Keycloak({
    url: 'http://keycloak-server/auth',
    realm: 'myrealm',
    clientId: 'myapp'
});

デフォルトでは login 関数を呼び出す必要があります。ただし、アダプターを自動的に認証させるための2つのオプションがあります。 init 関数に login-required または check-sso を渡すことができます。 login-required は、ユーザーがKeycloakにログインしていない場合にクライアントを認証し、そうでない場合にログインページを表示します。 check-sso は、ユーザーがすでにログインしている場合にのみクライアントを認証します。ユーザーがログインしていない場合、ブラウザーはアプリケーションにリダイレクトされ、未認証のままになります。

silent check-sso オプションを設定できます。この機能を有効にすると、ブラウザーはKeycloakサーバーへの完全なリダイレクトを行わず、アプリケーションに戻ります。このアクションは非表示のiframeで実行されるため、アプリケーションのリソースはアプリの初期化時にブラウザーによって1回だけロードと解析がされ、Keycloakからアプリへのリダイレクト・バック後に再度行われはしません。これは、SPA(シングル・ページ・アプリケーション)の場合に特に便利です。

silent check-sso を有効にするには、initメソッドで silentCheckSsoRedirectUri 属性を提供する必要があります。このURIは、アプリケーションの有効なエンドポイントである必要があります(もちろん、Keycloak管理コンソールでクライアントの有効なリダイレクトとして設定する必要があります)。

keycloak.init({
    onLoad: 'check-sso',
    silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html'
})

サイレント・チェックSSOリダイレクトURIのページは、認証状態を正常にチェックし、Keycloakサーバーからトークンを取得した後、iframeにロードされます。受信したトークンをメイン・アプリケーションに送信する以外のタスクはなく、次のように見えるはずです。

<html>
<body>
    <script>
        parent.postMessage(location.href, location.origin)
    </script>
</body>
</html>

指定されたlocationにあるこのページは、アプリケーション自体によって提供される必要があり、JavaScriptアダプターの一部ではないことに注意してください。

Silent check-sso 機能は、一部の最新ブラウザーで制限されています。トラッキング防止機能を備えた最新のブラウザーのセクションを参照してください。

login-required を有効にするには、以下のように onLoadlogin-required を設定し、 init メソッドに渡します。

keycloak.init({
    onLoad: 'login-required'
})

ユーザーが認証された後、アプリケーションは Authorization ヘッダーにベアラー・トークンを含めることによって、Keycloakによって保護されたRESTfulサービスへのリクエストを行うことができます。以下に例を示します。

var loadData = function () {
    document.getElementById('username').innerText = keycloak.subject;

    var url = 'http://localhost:8080/restful-service';

    var req = new XMLHttpRequest();
    req.open('GET', url, true);
    req.setRequestHeader('Accept', 'application/json');
    req.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);

    req.onreadystatechange = function () {
        if (req.readyState == 4) {
            if (req.status == 200) {
                alert('Success');
            } else if (req.status == 403) {
                alert('Forbidden');
            }
        }
    }

    req.send();
};

注意すべきことは、デフォルトではアクセス・トークンの有効期限が短いため、リクエストを送信する前にアクセス・トークンを更新する必要があることです。これは updateToken メソッドで行うことができます。 updateToken メソッドは、トークンが正常にリフレッシュされた場合にのみ、サービスの呼び出しを容易にするPromiseを返し、そうでない場合は、ユーザーにエラーを表示します。以下に例を示します。

keycloak.updateToken(30).then(function() {
    loadData();
}).catch(function() {
    alert('Failed to refresh token');
});

2.2.1. セッション・ステータスiframe

デフォルトでは、JavaScriptアダプターは、シングル・サイン・アウトが発生したかどうかを検出するために使用される非表示のiframeを作成します。これはネットワーク・トラフィックを必要とせず、代わりに特殊なステータス・クッキーを調べることによってステータスを取得します。この機能は、 init メソッドに渡されるオプションに checkLoginIframe: false を設定することで無効にできます。

このクッキーを直接参照することに頼るべきではありません。フォーマットは変更可能で、アプリケーションではなくKeycloakサーバーのURLに関連付けられています。

Session Status iframe機能は、一部の最新ブラウザーで制限されています。トラッキング防止機能を備えた最新のブラウザーのセクションを参照してください。

2.2.2. インプリシット・フローとハイブリッド・フロー

デフォルトで、JavaScriptアダプターは 認可コード フローを使用します。

このフローにより、Keycloakサーバーは認証トークンではなく、認可コードをアプリケーションに返します。ブラウザーがアプリケーションにリダイレクトされた後、JavaScriptアダプターは code をアクセス・トークンとリフレッシュ・トークンに交換します。

Keycloakは、Keycloakでの認証が成功した直後にアクセス・トークンが送信される インプリシット・フロー もサポートしています。これは、トークンに対してコードを交換する追加のリクエストが無いので、標準フローよりもパフォーマンスに優れる可能性がありますが、アクセス・トークンの有効期限が切れたときに影響があります。

ただし、URLフラグメントにアクセス・トークンを送信することはセキュリティー上の脆弱性となります。たとえば、Webサーバーのログやブラウザーの履歴からトークンが漏洩する可能性があります。

インプリシット・フローを有効にするには、Keycloak管理コンソールでクライアントの Implicit Flow Enabled フラグを有効にする必要があります。 init メソッドに implicit という値を持つ flow パラメータを渡す必要もあります:

keycloak.init({
    flow: 'implicit'
})

注意すべき点の1つは、アクセス・トークンのみが提供され、リフレッシュ・トークンがないことです。つまり、アクセス・トークンが期限切れになると、アプリケーションはKeycloakへのリダイレクトを再度実行して、新しいアクセス・トークンを取得する必要があります。

Keycloakは ハイブリッド・フロー もサポートしています。

これは、管理コンソールでクライアントの Standard Flow EnabledImplicit Flow Enabled フラグを有効にすることを要求します。Keycloakサーバーはコードとトークンの両方をアプリケーションに送信します。アクセス・トークンとリフレッシュ・トークンの交換ができる間、アクセス・トークンは即時使用できます。インプリシット・フローと同様に、アクセス・トークンがすぐに利用できるため、ハイブリッド・フローはパフォーマンスに優れています。しかし、トークンは引き続きURLに送信され、前述のセキュリティーの脆弱性が当てはまる可能性があります。

ハイブリッド・フローの1つの利点は、アプリケーションでリフレッシュ・トークンが利用可能になることです。

ハイブリッド・フローの場合、以下のようにパラメーター flow を値 hybridinit メソッドに渡す必要があります。

keycloak.init({
    flow: 'hybrid'
})

2.2.3. ハイブリッド・アプリとCordova

Keycloakは Apache Cordova で開発されたハイブリッド・モバイルアプリをサポートしています。JavaScriptアダプターには、 cordovacordova-native の2つのモードがあります。

デフォルトはcordovaで、アダプタータイプが設定されておらず、window.cordovaが存在する場合、アダプターは自動的に選択します。ログインすると、 InApp Browser が開き、ユーザーはKeycloakとやり取りした後、アプリを http://localhost にリダイレクトします。そのため、管理コンソールのクライアント設定のセクションで、このURLを有効なリダイレクトURIとしてホワイトリストに登録する必要があります。

このモードはセットアップが簡単ですが、いくつかの欠点もあります。

  • InApp-Browserは、アプリに組み込まれたブラウザーであり、携帯電話のデフォルト・ブラウザーではありません。したがって、設定が異なり、保存されたクレデンシャルは利用できません。

  • 特に複雑なテーマをレンダリングする場合、InApp-Browserの方が処理速度が遅くなる可能性があります。

  • このモードを使用する前に、アプリがログインページをレンダリングするブラウザーを完全に制御しているため、ユーザーのクレデンシャルにアクセスする可能性があるなど、セキュリティーを考慮する必要があります。そのため、信頼できないアプリでは使用を許可しないでください。

このサンプル・アプリケーションを使用して、開始します: https://github.com/keycloak/keycloak/tree/master/examples/cordova

代替モード cordova-native は異なるアプローチをとっています。システムのブラウザーを使用してログインページを開きます。ユーザーが認証されると、特別なURLを使用して、ブラウザーがアプリにリダイレクトされます。そこから、Keycloakアダプターは、URLからコードまたはトークンを読み取ってログインを完了できます。

ネイティブモードをアクティブにするには、次のようにアダプタータイプ cordova-nativeinit メソッドに渡します。

keycloak.init({
    adapter: 'cordova-native'
})

このアダプターには、次の2つの追加プラグインが必要でした。

アプリにリンクするための技術的詳細は、各プラットフォームで異なり、特別な設定が必要です。詳しい手順については、 ディープリンク・プラグインのドキュメント のAndroidおよびiOSのセクションを参照してください。

アプリを開くためのリンクには、カスタムスキーマ(つまり、 myapp://login または android-app://com.example.myapp/https/example.com/login )と ユニバーサル・リンク(iOS))/ ディープリンク(Android)をクリックします。前者は設定が容易で、より確実に動作する傾向がありますが、後者はセキュリティーが強化され、ドメインの所有者のみが登録できます。カスタムURLはiOSでは推奨されていません。最良の信頼性を得るために、ユニバーサル・リンクとそれにcustom-urlリンクを持つ代替サイトを組み合わせて使用することをお勧めします。

さらに、Keycloakアダプターとの互換性を向上させるには、以下の手順を実行することをお勧めします。

  • iOS上のユニバーサル・リンクは、 query-mode に設定された response-mode でより確実に動作するようです。

  • Androidがリダイレクト時にアプリケーションの新しいインスタンスをオープンすることを防ぐには、次のスニペットを config.xml に追加します。

<preference name="AndroidLaunchMode" value="singleTask" />

ネイティブモードを使用する方法を示すサンプル・アプリケーションがあります: https://github.com/keycloak/keycloak/tree/master/examples/cordova-native

2.2.4. カスタム・アダプター

デフォルトでサポートされていない環境(Capacitorなど)でJavaScriptクライアントを実行する必要がある場合があります。このような未知の環境でJavasScriptクライアントを使用できるようにするために、カスタム・アダプターを渡すことができます。たとえば、サードパーティーのライブラリーがそのようなアダプターを提供して、JavaScriptクライアントを問題なく実行できるようにすることができます。

import Keycloak from 'keycloak-js';
import KeycloakCapacitorAdapter from 'keycloak-capacitor-adapter';

const keycloak = new Keycloak();

keycloak.init({
    adapter: KeycloakCapacitorAdapter,
});

この特定のパッケージは存在しませんが、そのようなアダプターをクライアントに渡す方法の良い例を示しています。

独自のアダプターを作成することもできます。そのためには、 KeycloakAdapter インターフェイスで説明されているメソッドを実装する必要があります。たとえば、次のTypeScriptコードにより、すべてのメソッドが適切に実装されます。

import Keycloak, { KeycloakAdapter } from 'keycloak-js';

// Implement the 'KeycloakAdapter' interface so that all required methods are guaranteed to be present.
const MyCustomAdapter: KeycloakAdapter = {
    login(options) {
        // Write your own implementation here.
    }

    // The other methods go here...
};

const keycloak = new Keycloak();

keycloak.init({
    adapter: MyCustomAdapter,
});

もちろん、タイプ情報を省略してTypeScriptを使用せずにこれを行うこともできますが、インターフェイスを適切に実装することは、完全にあなた次第です。

2.2.5. 以前のブラウザー

JavaScriptアダプターは、Base64(window.btoaとwindow.atob)、HTML5 History API、およびオプションでPromise APIに依存しています。これらが利用できないブラウザー(IE9など)をサポートする必要がある場合は、Polyfillを追加する必要があります。

Polyfillライブラリーの例:

2.2.6. トラッキング防止を備えた最新のブラウザー

一部のブラウザーの最新バージョンでは、さまざまなCookieポリシーが適用され、ChromeのSameSiteやサードパーティーのCookieを完全にブロックするなど、サードパーティによるユーザーのトラッキングを防止しています。これらのポリシーは今後さらに制限が厳しくなり、他のブラウザによって採用され、最終的にサードパーティーのコンテキストのCookieが完全にサポートされなくなり、ブラウザーによってブロックされることが予想されます。これにより影響を受けるアダプター機能は、将来廃止される可能性があります。

JavaScriptアダプターは、Session Status iframe、 silent check-sso、および通常の(非サイレント) check-sso のサードパーティーCookieに依存しています。これらの機能は、機能が制限されているか、Cookieに関するブラウザーの制限に基づいて完全に無効になっています。アダプターはこの設定を検出しようとし、それに応じて反応します。

"SameSite=Lax by Default" ポリシーのブラウザー

SSL / TLS接続がKeycloak側とアプリケーション側で設定されている場合、すべての機能がサポートされます。 SSL / TLSの構成 を参照してください。影響を受けるのは バージョン84以降のChromeです。

サードパーティーのCookieがブロックされているブラウザー

セッション・ステータスiframeはサポートされておらず、そのようなブラウザーの動作がJSアダプターによって検出された場合は自動的に無効になります。つまり、アダプターはシングル・サインアウトの検出にセッションCookieを使用できず、純粋にトークンに依存する必要があります。これは、ユーザーが別のウィンドウでログアウトすると、JavaScriptアダプターを使用するアプリケーションは、アクセストークンをリフレッシュしようとするまでログアウトされないことを意味します。そのため、アクセストークンのライフスパンを比較的短い時間に設定して、ログアウトが検出されるのが遅くなるのを防ぐことを推奨します。 セッションとトークンのタイムアウト を参照してください。

Silent check-sso はサポートされておらず、デフォルトでは通常の(非サイレント) check-sso にフォールバックします。この動作は、 init メソッドに渡されるオプションで silentCheckSsoFallback: false を設定することで変更できます。この場合、ブラウザーの制限的な動作が検出されると、 check-sso は完全に無効になります。

通常の check-sso も影響を受けます。Session Status iframeはサポートされていないため、ユーザーのログイン・ステータスを確認するためにアダプターを初期化するときに、Keycloakへの追加のリダイレクトを行う必要があります。これは、iframeを使用してユーザーがログインしているかどうかを通知する標準の動作とは異なり、リダイレクトはログアウトした場合にのみ実行されます。

影響を受けるブラウザーは、バージョン13.1以降のSafariです。

2.2.7. JavaScriptアダプター・リファレンス

コンストラクター
new Keycloak();
new Keycloak('http://localhost/keycloak.json');
new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp' });
プロパティー
authenticated

ユーザーが認証されている場合は true 、それ以外の場合は false です。

token

サービスへのリクエストの Authorization ヘッダーで送信できるBase64でエンコードされたトークンです。

tokenParsed

JavaScriptオブジェクトとして解析されたトークン。

subject

ユーザーID。

idToken

Base64でエンコードされたIDトークン。

idTokenParsed

JavaScriptオブジェクトとして解析されたIDトークン。

realmAccess

トークンに関連付けられているレルムのロール。

resourceAccess

トークンに関連付けられているリソースのロール。

refreshToken

新しいトークンの取得に使用できるBase64でエンコードされたリフレッシュ・トークン。

refreshTokenParsed

JavaScriptオブジェクトとして解析されたリフレッシュ・トークン。

timeSkew

ブラウザーとKeycloakサーバーの推定される時間差(秒単位)。この値は単なる見積もりですが、トークンが期限切れになっているかどうかを判断するには十分正確です。

responseMode

initに渡されるレスポンス・モード(デフォルト値はfragment)。

flow

initに渡されるフロー。

アダプター

リダイレクトの方法やライブラリーにより処理されるその他のブラウザー関連の関数をオーバーライドすることができます。利用可能なオプションは以下のとおりです。

  • "default" - ライブラリーはリダイレクトにブラウザーのAPIを使用します(これはデフォルトです)

  • "cordova" - ライブラリーは、InAppBrowser cordovaプラグインを使用して、Keycloakのログイン/登録ページを読み込もうとします(これは、ライブラリーがcordovaエコシステムで動作しているときに自動的に使用されます)

  • "cordova-native" - ライブラリーはBrowserTabs cordovaプラグインを使用して、電話のシステム・ブラウザーを使用してログインページと登録ページを開こうとします。これには、アプリケーションにリダイレクトするための特別な設定が必要です(ハイブリッド・アプリとCordovaを参照してください)。

  • custom - カスタム・アダプターを実装することができます(高度なユースケースのみ)

responseType

Keycloakにログイン・リクエストとともに送信されたレスポンスタイプ。これは、初期化中に使用されたフロー値に基づいて決定されますが、この値を設定することで上書きできます。

メソッド
init(options)

アダプターを初期化するために呼び出されます。

optionsはオブジェクトで、以下のプロパティーがあります。

  • useNonce - 暗号化ノンスを追加して、認証レスポンスがリクエストと一致することを確認します(デフォルトは true です)。

  • onLoad - ロード時に実行するアクションを指定します。サポートされている値は login-required または check-sso です。

  • silentCheckSsoRedirectUri - onLoadが’check-sso’に設定されている場合、サイレント認証チェックのリダイレクトURIを設定します。

  • silentCheckSsoFallback - silent check-sso がブラウザーでサポートされていない場合、通常の check-sso へのフォールバックを有効にします(デフォルトは true です)。

  • token - トークンの初期値を設定します。

  • refreshToken - リフレッシュ・トークンの初期値を設定します。

  • idToken - IDトークンの初期値を設定します(tokenまたはrefreshTokenとともにする場合のみ)。

  • timeSkew - ローカルの時間とKeycloakサーバーとの間のスキューの初期値を秒単位で設定します(tokenまたはrefreshTokenとともにする場合のみ)。

  • checkLoginIframe - ログイン状態の監視を有効/無効に設定します(デフォルトは true )。

  • checkLoginIframeInterval - ログイン状態を確認する間隔を設定します(デフォルトは5秒)。

  • responseMode - ログイン・リクエストの時にKeycloakサーバーに送信するOpenID Connectレスポンス・モードを設定します。有効な値は、 query または fragment です。デフォルト値は fragment です。つまり、認証が成功した後、KeycloakはURLフラグメントに追加されたOpenID Connectパラメーターとともに、JavaScriptアプリケーションにリダイレクトされます。これは一般的に query よりも安全で推奨されます。

  • flow - OpenID Connectのフローを設定します。有効な値は、 standardimplicithybrid のいずれかです。

  • enableLogging - Keycloakからコンソールへのメッセージのロギングを有効にします(デフォルトは false )。

  • pkceMethod - Proof Key Code Exchange( PKCE )が使用するメソッド。この値を設定すると、PKCEメカニズムが有効になります。利用可能なオプションは、以下の通りです。

    • "S256" - SHA256ベースのPKCEメソッド

初期化が完了すると解決するPromiseを返します。

login(options)

ログイン・フォームにリダイレクトします(optionsは、redirectUriおよび/またはpromptフィールドを持つ任意のオブジェクトです)。

optionsはオブジェクトで、以下のプロパティーがあります。

  • redirectUri - ログイン後にリダイレクトするURIを指定します。

  • prompt - このパラメーターを使用すると、Keycloakサーバー側のログインフローを少しだけカスタマイズできます。たとえば、値が login の場合は、ログイン画面を表示するようにします。 prompt パラメーターの詳細とすべての値については、 パラメーター転送のセクション を参照してください。

  • maxAge - ユーザーがすでに認証されている場合にのみ使用されます。ユーザーの認証が行われてからの最大時間を指定します。ユーザーがすでに maxAge よりも長い時間認証済みの場合、SSOは無視され、再度認証する必要があります。

  • loginHint - ログイン・フォームのユーザー名/電子メール・フィールドを事前入力するために使用されます。

  • scope - Keycloakログイン・エンドポイントに、scopeパラメーターを転送するために使用します。スペース区切りのスコープのリストを使用します。それらは通常、特定のクライアントで定義されたClient scopesを参照します。スコープ openid は、アダプターによって常にスコープのリストに追加されることに注意してください。たとえば、スコープ・オプション address phone を入力すると、Keycloakへのリクエストにスコープ・パラメーター scope=openid address phone が含まれます。

  • idpHint - ログインページの表示をスキップし、代わりに指定されたアイデンティティー・プロバイダーに自動的にリダイレクトするように、Keycloakに指示するために使用されます。詳細は、Identity Provider documentationを参照してください。

  • action - 値が register の場合、ユーザーは登録ページにリダイレクトされ、そうでない場合はログイン・ページにリダイレクトされます。

  • locale - OIDC 1.0仕様のセクション3.1.2.1 に準拠した 'ui_locales' クエリー・パラメーターを設定します。

  • cordovaOptions - Cordovaのアプリケーション内ブラウザーに渡される引数を指定します(該当する場合)。オプション hiddenlocation はこれらの引数の影響を受けません。利用可能なすべてのオプションは https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-inappbrowser/ で定義されています。使用例: { zoom: "no", hardwareback: "yes" }

createLoginUrl(options)

ログイン・フォームへのURLを返します(optionsは、redirectUriおよび/またはpromptフィールドを持つ任意のオブジェクトです)。

optionsはオブジェクトで、 login 関数と同様のオプションをサポートします。

logout(options)

ログアウトにリダイレクトします。

optionsはオブジェクトで、以下のプロパティーがあります。

  • redirectUri - ログアウト後にリダイレクトするURIを指定します。

createLogoutUrl(options)

ユーザーをログアウトするURLを返します。

optionsはオブジェクトで、以下のプロパティーがあります。

  • redirectUri - ログアウト後にリダイレクトするURIを指定します。

register(options)

登録フォームにリダイレクトします。オプション action = 'register' でのloginメソッドのショートカットです。

optionsはloginメソッドと同じですが、 'action' は 'register' に設定されています。

createRegisterUrl(options)

登録ページのURLを返します。オプション action = 'register' でのcreateLoginUrlメソッドのショートカットです。

optionsは createLoginUrlメソッドと同じですが、 'action' は 'register' に設定されています。

accountManagement()

アカウント管理コンソールにリダイレクトします。

createAccountUrl()

アカウント管理コンソールのURLを返します。

hasRealmRole(role)

トークンに指定されたレルム・ロールがある場合は、trueを返します。

hasResourceRole(role, resource)

トークンに指定されたresourceのロールがある場合は、trueを返します(resourceはオプションであり、指定されていない場合はclientIdが使用されます)。

loadUserProfile()

ユーザーのプロファイルを読み込みます。

プロファイルで解決されるPromiseを返します。

例:

keycloak.loadUserProfile()
    .then(function(profile) {
        alert(JSON.stringify(profile, null, "  "))
    }).catch(function() {
        alert('Failed to load user profile');
    });
isTokenExpired(minValidity)

トークンが期限切れになる前にminValidity秒を下回っている場合はtrueを返します(指定されていない場合はminValidityはオプションです。0が使用されます)。

updateToken(minValidity)

トークンがminValidity秒以内に期限切れになると(minValidityは省略可能です。指定されていない場合は5が使用されます)、トークンがリフレッシュされます。セッション・ステータスiframeが有効な場合、セッション・ステータスもチェックされます。

トークンがリフレッシュされたかどうかを示すブール値で解決されるPromiseを返します。

例:

keycloak.updateToken(5)
    .then(function(refreshed) {
        if (refreshed) {
            alert('Token was successfully refreshed');
        } else {
            alert('Token is still valid');
        }
    }).catch(function() {
        alert('Failed to refresh the token, or the session has expired');
    });
clearToken()

認証状態(トークンを含む)をクリアします。これは、トークンの更新が失敗した場合など、セッションが終了したことをアプリケーションが検出した場合に役立ちます。

これを呼び出すと、onAuthLogoutコールバック・リスナーが呼び出されます。

コールバック・イベント

アダプターは、特定のイベントのコールバック・リスナーの設定をサポートします。

例:

keycloak.onAuthSuccess = function() { alert('authenticated'); }

使用できるイベントは次のとおりです。

  • onReady(authenticated) - アダプターが初期化されたときに呼び出されます。

  • onAuthSuccess - ユーザーが正常に認証されたときに呼び出されます。

  • onAuthError - 認証時にエラーが発生した場合に呼び出されます。

  • onAuthRefreshSuccess - トークンがリフレッシュされたときに呼び出されます。

  • onAuthRefreshError - トークンをリフレッシュする際にエラーが発生した場合に呼び出されます。

  • onAuthLogout - ユーザーがログアウトしたときに呼び出されます(セッション・ステータスiframeが有効な場合、またはCordovaモードの場合にのみ、呼び出されます)。

  • onTokenExpired - アクセス・トークンが期限切れになったときに呼び出されます。リフレッシュ・トークンが利用可能な場合、トークンはupdateTokenでリフレッシュすることができます。リフレッシュ・トークンが利用できない場合(つまり、インプリシット・フローの場合)、ログイン画面にリダイレクトして新しいアクセス・トークンを取得できます。

2.3. Node.jsアダプター

Keycloakは、サーバーサイドのJavaScriptアプリケーションを保護するために、 Connect の上に構築されたNode.jsアダプターを提供します。目標は、 Express.js などのフレームワークと統合するのに十分な柔軟性を得ることです。

ライブラリーは Keycloak organization から直接ダウンロードすることができ、ソースは GitHub で利用可能です。

Node.jsアダプターを使用するには、まず、Keycloak管理コンソールでアプリケーションのクライアントを作成する必要があります。アダプターは、public、confidential、bearer-onlyのアクセス・タイプをサポートします。どれを選択するかは、ユースケースのシナリオに依存します。

クライアントが作成されたら、 Installation タブをクリックし、 Format OptionKeycloak OIDC JSON を選択し、 Download をクリックします。ダウンロードした keycloak.json ファイルはプロジェクトのルート・フォルダーに配置します。

2.3.1. インストール

すでに Node.js がインストールされていると仮定して、アプリケーション用のフォルダーを作成します。

mkdir myapp && cd myapp

npm init コマンドを使ってアプリケーション用の package.json を作成してください。依存関係リストにKeycloak接続アダプターを追加します。

    "dependencies": {
        "keycloak-connect": "11.0.0"
    }

2.3.2. 使い方

Keycloakクラスのインスタンスの作成

Keycloak クラスは、アプリケーションの設定と統合のための中心的なポイントを提供します。最も簡単な作成では引数はありません。

    var session = require('express-session');
    var Keycloak = require('keycloak-connect');

    var memoryStore = new session.MemoryStore();
    var keycloak = new Keycloak({ store: memoryStore });

デフォルトでは、keycloak固有の設定(公開鍵、レルム名、さまざまなURL)を初期化するために、アプリケーションのメイン実行可能ファイルの横に keycloak.json という名前のファイルがあります。 keycloak.json ファイルは、Keycloak管理者コンソールから取得できます。

このメソッドでインスタンス化すると、合理的なデフォルトが使用されます。代替として、 keycloak.json ファイルではなく、次のように設定オブジェクトを提供することも可能です。

    let kcConfig = {
        clientId: 'myclient',
        bearerOnly: true,
        serverUrl: 'http://localhost:8080/auth',
        realm: 'myrealm',
        realmPublicKey: 'MIIBIjANB...'
    };

    let keycloak = new Keycloak({ store: memoryStore }, kcConfig);

アプリケーションは、次の方法を使用して、ユーザーを優先度の高いアイデンティティー・プロバイダーにリダイレクトすることもできます。

    let keycloak = new Keycloak({ store: memoryStore, idpHint: myIdP }, kcConfig);
Webセッションストアの設定

認証のために、Webセッションを使用してサーバーサイドの状態を管理する場合は、少なくとも store パラメータで Keycloak(…​) を初期化し、実際のセッションストアで express-session 使用する必要があります。

    var session = require('express-session');
    var memoryStore = new session.MemoryStore();

    var keycloak = new Keycloak({ store: memoryStore });
カスタムスコープ値を渡す

デフォルトでは、スコープ値 openid はクエリー・パラメーターとしてKeycloakのログインURLに渡されますが、次のようにカスタム値を新たに追加することもできます。

    var keycloak = new Keycloak({ scope: 'offline_access' });

2.3.3. Middlewareのインストール

インスタンス化が完了したら、Middlewareをconnectに対応したアプリケーションにインストールします。

    var app = express();

    app.use( keycloak.middleware() );

2.3.4. プロキシーの設定

SSL接続を終了するプロキシーの背後でアプリケーションが実行されている場合は、 express behind proxies ガイドに従ってExpressを設定する必要があります。不適切なプロキシー設定を使用すると、無効なリダイレクトURIが生成される可能性があります。

設定例:

    var app = express();

    app.set( 'trust proxy', true );

    app.use( keycloak.middleware() );

2.3.5. 認証のチェック

リソースにアクセスする前にユーザーが認証されていることを確認するには、単に keycloak.checkSso() を使います。ユーザーがすでにログインしている場合にのみ認証されます。ユーザーがログインしていない場合、ブラウザーは最初に要求されたURLにリダイレクトされ、ユーザーは未認証のままになります。

    app.get( '/check-sso', keycloak.checkSso(), checkSsoHandler );

2.3.6. リソースの保護

単純な認証

リソースにアクセスする前にユーザーの認証を強制するには、引数のないバージョンの keycloak.protect() を使うだけです。

    app.get( '/complain', keycloak.protect(), complaintHandler );
ロールベースの認可

現在のアプリケーションのアプリケーション・ロールでリソースを保護するには次のようにします。

    app.get( '/special', keycloak.protect('special'), specialHandler );

別の アプリケーションのアプリケーション・ロールでリソースを保護するには次のようにします。

    app.get( '/extra-special', keycloak.protect('other-app:special'), extraSpecialHandler );

レルムロールを使用してリソースを保護するには次のようにします。

    app.get( '/admin', keycloak.protect( 'realm:admin' ), adminHandler );
リソースベースの認可

リソースベースの認可では、Keycloakで定義されている一連のポリシーに基づいてリソースとその特定のメソッド/アクションを保護することができます。したがって、アプリケーションからの認可を外部化できます。これは、リソースを保護するために使用できる keycloak.enforcer メソッドを公開することによって達成されます。

    app.get('/apis/me', keycloak.enforcer('user:profile'), userProfileHandler);

keycloak-enforcer メソッドは、 response_mode 設定オプションの値に応じて2つのモードで動作します。

    app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), userProfileHandler);

response_modetoken に設定されている場合、パーミッションはアプリケーションに送られたベアラートークンによって表されるサブジェクトに代わりにサーバーから取得されます。この場合、Keycloakによって付与されたパーミッションとともに新しいアクセストークンが発行されます。サーバーが想定したパーミッションを持つトークンで応答しなかった場合、リクエストは拒否されます。このモードを使用するときは、次のようにリクエストからトークンを取得できるはずです。

    app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), function (req, res) {
        var token = req.kauth.grant.access_token.content;
        var permissions = token.authorization ? token.authorization.permissions : undefined;

        // show user profile
    });

アプリケーションがセッションを使用していて、サーバーからの以前の決定をキャッシュしたい場合や、自動的に更新トークンを処理したい場合は、このモードを選択してください。このモードは、クライアントおよびリソースサーバーとして機能するアプリケーションに特に役立ちます。

response_modepermissions に設定されている場合(デフォルトモード)、サーバーは新しいアクセストークンを発行せずに、許可されたパーミッションのリストのみを返します。新しいトークンを発行しないことに加えて、このメソッドは以下のように request を介してサーバーにより与えられたパーミッションを公開します。

    app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'permissions'}), function (req, res) {
        var permissions = req.permissions;

        // show user profile
    });

response_mode が使われているかどうかにかかわらず、 keycloak.enforcer メソッドは最初にアプリケーションに送られたベアラートークンの中のパーミッションをチェックしようとします。ベアラートークンがすでに想定されたパーミッションを持っている場合は、決定を得るためにサーバーと対話する必要はありません。これは、保護されたリソースにアクセスする前に、クライアントが期待されるパーミッションでサーバーからアクセストークンを取得できる場合に特に役立ちます。そのため、クライアントは増分認可などのKeycloak認可サービスによって提供される機能を使用できます。 keycloak.enforcer はリソースへのアクセスを強制します。

デフォルトでは、ポリシー・エンフォーサーはアプリケーションに定義された client_id を使って(例えば keycloak.json を介して)、Keycloakの認可サービスをサポートするKeycloakのクライアントを参照します。この場合、クライアントは実際にはリソースサーバーであるため、パブリックにすることはできません。

アプリケーションがパブリック・クライアント(フロントエンド)とリソースサーバー(バックエンド)の両方として機能している場合は、次の設定を使用して、施行したいポリシーでKeycloak内の別のクライアントを参照できます。

      keycloak.enforcer('user:profile', {resource_server_id: 'my-apiserver'})

フロントエンドとバックエンドを表すために、Keycloakで個別のクライアントを使用することをお勧めします。

保護しているアプリケーションがKeycloak認可サービスで有効になっていて、 keycloak.json でクライアントのクレデンシャルを定義している場合は、決定を下すために、追加のクレームをサーバーにプッシュし、それらをポリシーで利用できるようにすることができます。そのために、プッシュしたいクレームを持つJSONを返す function を期待する claim 設定オプションを定義できます。

      app.get('/protected/resource', keycloak.enforcer(['resource:view', 'resource:write'], {
          claims: function(request) {
            return {
              "http.uri": ["/protected/resource"],
              "user.agent": // get user agent  from request
            }
          }
        }), function (req, res) {
          // access granted

アプリケーションのリソースを保護するようにKeycloakを設定する方法の詳細については、 Authorization Services Guide をご覧ください。

高度な認可

URLの一部に基づいてリソースを保護するには次のようにします(各セクションにロールが存在すると仮定します)。

    function protectBySection(token, request) {
      return token.hasRole( request.params.section );
    }

    app.get( '/:section/:page', keycloak.protect( protectBySection ), sectionHandler );

2.3.7. 追加のURL

明示的なユーザー・トリガー・ログアウト

デフォルトでは、Middlewareは /logout の呼び出しをキャッチし、ユーザーにKeycloak中心のログアウト・ワークフローを経由させます。これは、 logout 設定パラメーターを middleware() の呼び出しに指定することで変更できます。

    app.use( keycloak.middleware( { logout: '/logoff' } ));

ユーザートリガーのログアウトが呼び出されると、次のようにクエリー・パラメーター redirect_url を渡すことができます。

https://example.com/logoff?redirect_url=https%3A%2F%2Fexample.com%3A3000%2Flogged%2Fout

このパラメーターはOIDCログアウト・エンドポイントのリダイレクトURLとして使用され、ユーザーは https://example.com/logged/out にリダイレクトされます。

Keycloak Adminコールバック

また、MiddlewareはKeycloakコンソールからのコールバックをサポートしており、単一セッションまたはすべてのセッションをログアウトします。デフォルトでは、これらのタイプのAdminコールバックは / のルートURLを基準に発生しますが、 admin パラメーターを middleware() の呼び出しに与えることで変更できます。

    app.use( keycloak.middleware( { admin: '/callbacks' } );

2.4. mod_auth_openidc Apache HTTPDモジュール

mod_auth_openidcは、OpenID Connect用のApache HTTPプラグインです。現在利用している言語/環境でApache HTTPDをプロキシーとして使用できる場合は、 mod_auth_openidc を使用してOpenID ConnectでWebアプリケーションを保護することができます。このモジュールの設定は、このドキュメントの範囲を超えています。設定の詳細については、 mod_auth_openidc のGitHubリポジトリーを参照ください。

mod_auth_openidc を設定するには、以下が必要です。

  • client_id。

  • client_secret。

  • アプリケーションへのredirect_uri。

  • Keycloak openid-configuration URL

  • mod_auth_openidc 固有のApache HTTPDモジュールの設定。

設定例は次のようになります。

LoadModule auth_openidc_module modules/mod_auth_openidc.so

ServerName ${HOSTIP}

<VirtualHost *:80>

    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html

    #this is required by mod_auth_openidc
    OIDCCryptoPassphrase a-random-secret-used-by-apache-oidc-and-balancer

    OIDCProviderMetadataURL ${KC_ADDR}/auth/realms/${KC_REALM}/.well-known/openid-configuration

    OIDCClientID ${CLIENT_ID}
    OIDCClientSecret ${CLIENT_SECRET}
    OIDCRedirectURI http://${HOSTIP}/${CLIENT_APP_NAME}/redirect_uri

    # maps the prefered_username claim to the REMOTE_USER environment variable
    OIDCRemoteUserClaim preferred_username

    <Location /${CLIENT_APP_NAME}/>
        AuthType openid-connect
        Require valid-user
    </Location>
</VirtualHost>

mod_auth_openidcの設定方法の詳細については、 mod_auth_openidcプロジェクトページを参照してください。

2.5. 他のOpenID Connectライブラリー

Keycloakは、使いやすくて、Keycloakと統合しやすい付属のアダプターにより、セキュリティー保護できます。ただし、使用するプログラミング言語、フレームワーク、プラットフォームでアダプターが使用できない場合は、代わりに一般的なOpenID ConnectのRelying Party(RP)ライブラリーを使用することができます。この章では、Keycloak固有のことについて詳細に説明しますが、特定のプロトコルの詳細については言及しません。詳細については、 OpenID Connectの仕様OAuth2の仕様 を参照してください。

2.5.1. エンドポイント

理解すべき最も重要なエンドポイントは、 well-known 設定エンドポイントです。これは、KeycloakのOpenID Connectの実装に関連するエンドポイントとその他の設定オプションを一覧化します。エンドポイントは次のとおりです。

/realms/{realm-name}/.well-known/openid-configuration

完全なURLを取得するには、KeycloakのベースURLを追加し、 {realm-name} をレルム名で置換します。以下に例を示します。

http://localhost:8080/auth/realms/master/.well-known/openid-configuration

一部のRPライブラリーは、このエンドポイントから必要なすべてのエンドポイントを取得しますが、他はエンドポイントを個別に記載する必要があります。

認可エンドポイント
/realms/{realm-name}/protocol/openid-connect/auth

認可エンドポイントは、エンドユーザーの認証を実行します。これは、ユーザー・エージェントをこのエンドポイントにリダイレクトすることによって行われます。

詳細については、OpenID Connectの仕様の Authorization Endpoint を参照してください。

トークン・エンドポイント
/realms/{realm-name}/protocol/openid-connect/token

トークン・エンドポイントは、トークンを取得するために使用されます。トークンは、認可コードと交換するか、使用されているフロー次第ではクレデンシャルを直接指定することによって取得できます。トークン・エンドポイントは、有効期限が切れたときに新しいアクセス・トークンを取得するためにも使用されます。

詳細については、OpenID Connectの仕様の Token Endpoint を参照してください。

Userinfoエンドポイント
/realms/{realm-name}/protocol/openid-connect/userinfo

UserInfoエンドポイントは、認証されたユーザーに関する標準クレームを返します。また、ベアラー・トークンによって保護されています。

詳細については、OpenID Connectの仕様の Userinfo Endpointを参照してください。

ログアウト・エンドポイント
/realms/{realm-name}/protocol/openid-connect/logout

ログアウト・エンドポイントは、認証されたユーザーをログアウトします。

ユーザー・エージェントはこのエンドポイントにリダイレクトされた場合、アクティブ・ユーザー・セッションはログアウトされます。その後、ユーザー・エージェントはアプリケーションにリダイレクトされます。

エンドポイントは、アプリケーションによって直接呼び出すこともできます。このエンドポイントを直接呼び出すには、リフレッシュ・トークンと、クライアント認証に必要なクレデンシャルを含める必要があります。

証明書エンドポイント
/realms/{realm-name}/protocol/openid-connect/certs

証明書エンドポイントは、JSON Web Key(JWK)としてエンコードされ、レルムで有効化されている公開鍵を返します。レルム設定次第で、トークンを検証するために1つ以上のキーを有効にすることができます。詳細については、Server Administration GuideJSON Web Keyの仕様 を参照してください。

イントロスペクション・エンドポイント
/realms/{realm-name}/protocol/openid-connect/token/introspect

イントロスペクション・エンドポイントは、トークンのアクティブ状態を取得するために使用されます。言い換えると、アクセストークンやリフレッシュトークンの検証に使用できます。これはコンフィデンシャル・クライアントによってのみ呼び出すことができます。

このエンドポイントで呼び出す方法の詳細については、 OAuth 2.0 Token Introspectionの仕様 を参照してください。

動的クライアント登録エンドポイント
/realms/{realm-name}/clients-registrations/openid-connect

動的クライアント登録エンドポイントを使用して、クライアントを動的に登録します。

詳細については、クライアントの登録の章OpenID Connect Dynamic Client Registrationの仕様 を参照してください。

トークン無効化エンドポイント
/realms/{realm-name}/protocol/openid-connect/revoke

トークン無効化エンドポイントは、トークンを無効化するために使用されます。このエンドポイントでは、リフレッシュトークンとアクセストークンの両方がサポートされています。

このエンドポイントで呼び出す方法の詳細については、 OAuth 2.0 Token Revocationの仕様 を参照してください。

デバイス認可エンドポイント
/realms/{realm-name}/protocol/openid-connect/auth/device

デバイス認可エンドポイントは、デバイスコードとユーザーコードを取得するために使用されます。コンフィデンシャル・クライアントのみが呼び出すことができます。

このエンドポイントで呼び出す方法の詳細については、 OAuth 2.0 Device Authorization Grantの仕様 を参照してください。

2.5.2. アクセストークンの検証

Keycloakによって発行されたアクセストークンを手動で検証する必要がある場合は、イントロスペクション・エンドポイントを呼び出すことができます。このアプローチの欠点は、Keycloakサーバーへのネットワーク呼び出しが必要であることです。これは、同時に実行される検証リクエストが多すぎると、サーバーの速度を低下させ、過剰な負荷をかかる可能性があります。Keycloakが発行したアクセストークンは JSON Web Tokens(JWT) であり、 JSON Web Signature(JWS) によりデジタル署名されています。このようにエンコードされているため、発行したレルムの公開鍵を使用してアクセストークンをローカルで検証できます。レルムの公開鍵を検証コードにハードコードするか、証明書エンドポイントを使用してJWSに埋め込まれたKey ID(KID)を使用して公開鍵を検索してキャッシュすることができます。コーディングする言語に応じて、JWS検証の手助けをするサードパーティー・ライブラリーが多数あります。

2.5.3. フロー

認可コード

認可コード・フローは、ユーザー・エージェントをKeycloakにリダイレクトします。ユーザーがKeycloakで正常に認証されると、認可コードが作成され、ユーザー・エージェントはアプリケーションにリダイレクトされます。次に、アプリケーションは、クレデンシャルとともに認可コードを使用して、Keycloakからアクセス・トークン、リフレッシュ・トークン、IDトークンを取得します。

このフローはWebアプリケーションを対象としていますが、ユーザー・エージェントを組み込むことができるモバイルアプリケーションなどのネイティブアプリケーションにも推奨されます。

詳細については、OpenID Connectの仕様の Authorization Code Flow を参照してください。

インプリシット

インプリシット・フローのリダイレクトは、認可コードフローと同様に機能しますが、認可コードが返される代わりに、アクセストークンとIDトークンが返されます。これにより、アクセストークンのために認可コードを交換するための余分な呼び出しの必要性が減ります。ただし、リフレッシュトークンは含まれません。その結果、長い有効期限を持つアクセストークンを許可する必要があります。これらのトークンを無効にするのは難しいため、問題があります。または、初期アクセストークンが期限切れになった場合は、新しいアクセス・トークンを取得するためにリダイレクトが必要です。インプリシット・フローは、アプリケーションがユーザーの認証のみを行い、自身でログアウトを処理する場合に便利です。

アクセス・トークンと認可コードが返されるハイブリッド・フローもあります。

インプリシット・フローとハイブリッド・フローの両方で、Webサーバーのログとブラウザーの履歴を通じてアクセス・トークンが漏洩する可能性があるため、潜在的なセキュリティー上のリスクがあることに注意してください。これは、アクセス・トークンの有効期限を短くすることで多少緩和されます。

詳細については、OpenID Connectの仕様の Implicit Flow を参照してください。

リソース・オーナー・パスワード・クレデンシャル

リソース・オーナー・パスワード・クレデンシャル(Keycloakではダイレクト・グラントと呼ばれます)は、ユーザー・クレデンシャルとトークンの交換を可能にします。絶対に必要としない限り、このフローを使用することはお勧めしません。これが有用な場合の例は、レガシー・アプリケーションとコマンドライン・インターフェイスです。

このフローの使用には次のような制限があります。

  • ユーザーのクレデンシャルがアプリケーションに公開されます

  • アプリケーションはログイン・ページが必要です

  • アプリケーションは認証方式を意識する必要があります

  • 認証フローを変更するには、アプリケーションを変更する必要があります

  • アイデンティティー・ブローカリングまたはソーシャル・ログインはサポートされません

  • フローはサポートされていません(ユーザーの自己登録、必須アクションなど)

リソース・オーナー・パスワード・クレデンシャルの使用が許可されるためには、クライアントは Direct Access Grants Enabled オプションを有効にする必要があります。

このフローはOpenID Connectには含まれていませんが、OAuth 2.0の仕様の一部です。

詳細については、OAuth 2.0仕様の Resource Owner Password Credentials Grant のセクションを参照してください。

CURLを使用した例

次の例は、ユーザー名 user とパスワード password を使用して、 master レルムにあるユーザーのアクセス・トークンを取得する方法を示しています。この例では、コンフィデンシャル・クライアント myclient を使用しています。

curl \
  -d "client_id=myclient" \
  -d "client_secret=40cc097b-2a57-4c17-b36a-8fdf3fc2d578" \
  -d "username=user" \
  -d "password=password" \
  -d "grant_type=password" \
  "http://localhost:8080/auth/realms/master/protocol/openid-connect/token"
クライアント・クレデンシャル

クライアント・クレデンシャルは、クライアント(アプリケーションおよびサービス)がユーザーの代わりにではなく、自分のためにアクセス権を取得したい場合に使用されます。これは、たとえば、特定のユーザーのためにではなく一般的にシステムに変更を適用するバックグラウンド・サービスに役立ちます。

Keycloakは、クライアントがシークレットまたは公開鍵/秘密鍵のいずれかで認証することをサポートします。

このフローはOpenID Connectには含まれていませんが、OAuth 2.0の仕様の一部です。

詳細については、OAuth 2.0仕様の Client Credentials Grant のセクションを参照してください。

デバイス認可グラント

デバイス認可グラントは、入力機能が制限されているか、適切なブラウザーがない、インターネットに接続されたデバイスで実行されているクライアントによって使用されます。アプリケーションはKeycloakにデバイスコードとユーザーコードを要求します。Keycloakは、デバイスコードとユーザーコードを作成します。Keycloakは、デバイスコードとユーザーコードを含むレスポンスをアプリケーションに返します。次に、アプリケーションはユーザーにユーザーコードと検証URIを提供します。ユーザーは、別のブラウザーを使用して、認証を受けるための検証URIにアクセスします。アプリケーションは、Keycloakがユーザーの認可を完了するまで、繰り返しKeycloakをポーリングします。ユーザーの認可が完了すると、アプリケーションはデバイスコードを取得します。次に、アプリケーションはデバイスコードとそのクレデンシャルを使用して、Keycloakからアクセストークン、リフレッシュトークン、およびIDトークンを取得します。

詳細については、https://tools.ietf.org/html/rfc8628 [OAuth 2.0 Device Authorization Grantの仕様] を参照してください。

2.5.4. リダイレクトURI

リダイレクト・ベースのフローを使用する場合は、クライアントで有効なリダイレクトURLを使用することが重要です。リダイレクトURLはできるだけ具体的にする必要があります。これは、特にクライアント・サイド(パブリック・クライアント)のアプリケーションに適用されます。そうしないと次のことが起こる可能性があります。

  • オープン・リダイレクト - これにより、攻撃者はあなたのドメインでできている偽のリンクを作成することができます

  • 不正侵入 - ユーザーがKeycloakですでに認証済みの場合、攻撃者はリダイレクトURLが正しく設定されていない公開クライアントを使用し、ユーザーを知らないうちにリダイレクトさせることで、アクセス権を得ます

Webアプリケーション用のプロダクション環境では、常にすべてのリダイレクトURIに https を使用します。httpへのリダイレクトは許可しないでください。

また、次のような特別なリダイレクトURIがいくつかあります。

http://localhost

このリダイレクトURIはネイティブ・アプリケーションに役立ちます。ネイティブ・アプリケーションが認可コードを取得するために、ランダムなポート上にWebサーバーを作成することを許可します。このリダイレクトURIは任意のポートを許可します。

urn:ietf:wg:oauth:2.0:oob

クライアントでWebサーバーを起動できない(またはブラウザーが利用できない)場合は、特殊な urn:ietf:wg:oauth:2.0:oob リダイレクトURIを使用することができます。このリダイレクトURIが使用されると、Keycloakは認可コードをページのタイトルとボックスに含めたページを表示します。アプリケーションがブラウザーのタイトルが変更されたことを検出するか、ユーザーが手動でコードをアプリケーションにコピー/ペーストすることができます。このリダイレクトURIを使用すると、ユーザーが別のデバイスを使用してアプリケーションに貼り付けるコードを取得することもできます。

3. SAML

このセクションでは、Keycloakクライアント・アダプターまたは汎用SAMLプロバイダー・ライブラリーのいずれかを使用して、アプリケーションとサービスをSAMLでセキュリティー保護する方法について説明します。

3.1. Javaアダプター

Keycloakには、Javaアプリケーション用のさまざまなアダプターが付属しています。正しいアダプターの選択は、対象のプラットフォームによって異なります。

3.1.1. 共通アダプター設定

KeycloakでサポートされているSAMLクライアント・アダプターは、いずれも簡単なXMLテキストファイルで設定できます。これは以下のようなものです。

<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
                       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                       xsi:schemaLocation="urn:keycloak:saml:adapter https://www.keycloak.org/schema/keycloak_saml_adapter_1_10.xsd">
    <SP entityID="http://localhost:8081/sales-post-sig/"
        sslPolicy="EXTERNAL"
        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
        logoutPage="/logout.jsp"
        forceAuthentication="false"
        isPassive="false"
        turnOffChangeSessionIdOnLogin="false"
        autodetectBearerOnly="false">
        <Keys>
            <Key signing="true" >
                <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
                    <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
                    <Certificate alias="http://localhost:8080/sales-post-sig/"/>
                </KeyStore>
            </Key>
        </Keys>
        <PrincipalNameMapping policy="FROM_NAME_ID"/>
        <RoleIdentifiers>
            <Attribute name="Role"/>
        </RoleIdentifiers>
        <RoleMappingsProvider id="properties-based-role-mapper">
            <Property name="properties.resource.location" value="/WEB-INF/role-mappings.properties"/>
        </RoleMappingsProvider>
        <IDP entityID="idp"
             signaturesRequired="true">
        <SingleSignOnService requestBinding="POST"
                             bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
                    />

            <SingleLogoutService
                    requestBinding="POST"
                    responseBinding="POST"
                    postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
                    redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
                    />
            <Keys>
                <Key signing="true">
                    <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
                        <Certificate alias="demo"/>
                    </KeyStore>
                </Key>
            </Keys>
        </IDP>
     </SP>
</keycloak-saml-adapter>

これらの設定の中には、アダプター固有のものもあれば、すべてのアダプターに共通のものもあります。Javaアダプターの場合は、システム・プロパティーの置換に ${…​} エンクロージャーを使用できます。 たとえば、 ${jboss.server.config.dir} です。

SP要素

ここでは、SP要素の属性の説明をします。

<SP entityID="sp"
    sslPolicy="ssl"
    nameIDPolicyFormat="format"
    forceAuthentication="true"
    isPassive="false"
    keepDOMAssertion="true"
    autodetectBearerOnly="false">
...
</SP>
entityID

このクライアントの識別子です。IdPは、IdPと通信しているクライアントを特定するため、この値を必要とします。この設定は、REQUIRED です。

sslPolicy

これは、アダプターが実施するSSLポリシーです。有効な値は ALLEXTERNALNONE です。 ALL の場合、すべてのリクエストはHTTPSを経由しなければなりません。 EXTERNAL の場合、非プライベートIPアドレスだけがHTTPSを経由しなければなりません。 NONE の場合、HTTPS経由であることは要求されません。この設定は OPTIONAL です。デフォルト値は EXTERNAL です。

nameIDPolicyFormat

SAMLクライアントは、特定のNameID Subject形式を要求できます。特定の書式が必要な場合は、この値を入力します。これは標準のSAML形式の識別子である urn:oasis:names:tc:SAML:2.0:nameid-format:transient でなければなりません。この設定は OPTIONAL です。デフォルトでは、特別な形式は要求されません。

forceAuthentication

SAMLクライアントは、すでにIdPにログインしていても、ユーザーが再認証されるように要求できます。有効にするには、これを true に設定します。この設定は OPTIONAL です。デフォルト値は false です。

isPassive

SAMLクライアントは、IdPにログインしていなくても、ユーザーに認証を要求しないようにすることができます。その場合は、 true に設定してください。 forceAuthentication と反対の設定なため一緒に使用しないでください。この設定は OPTIONAL です。デフォルト値は false です。

turnOffChangeSessionIdOnLogin

セッションIDは、セキュリティー攻撃口とならないように、一部のプラットフォームで正常にログインするとデフォルトで変更されます。これを無効にするには、 true に変更してください。これは無効にしないことをお勧めします。デフォルト値は false です。

autodetectBearerOnly

アプリケーションがWebアプリケーションとWebサービス(SOAPやRESTなど)の両方を提供する場合は、これを true に設定する必要があります。 Webアプリケーションの未認証のユーザーをKeycloakのログインページにリダイレクトできますが、ログインページへのリダイレクトを理解できない未認証のSOAPクライアントまたはRESTクライアントにはHTTPの 401 ステータスコードを送信します。Keycloakは、 X-Requested-WithSOAPActionAccept のような典型的なヘッダーに基づいて、SOAPクライアントまたはRESTクライアントを自動検出します。デフォルト値は false です。

logoutPage

これは、ログアウト後に表示するページを設定します。ページが http://web.example.com/logout.html のような完全なURLである場合、ログアウトすると、HTTP 302 ステータスコードを使用してそのページにリダイレクトされます。 /logout.jsp のようなスキーム部分を持たないリンクが指定された場合、 web.xmlの security-constraint 宣言に従って保護されたレルムにあるかどうかに関わらず、 ログアウト後にそのページが表示されます。このページは、デプロイメント・コンテキスト・ルートに関連して解決されます。

keepDOMAssertion

アダプターにアサーションのDOM表現を、リクエストに関連付けられた SamlPrincipal 内に元の形式で保存させるには、この属性を true に設定する必要があります。アサーション・ドキュメントは、プリンシパル内のメソッド getAssertionDocument を使用して取得できます。これは、署名されたアサーションを再生するときに特に便利です。返されるドキュメントは、Keycloakサーバーが受信したSAMLレスポンスを解析して生成されたドキュメントです。この設定は OPTIONAL で、デフォルト値は false です(ドキュメントはプリンシパル内に保存されません)。

サービス・プロバイダーのKeysとKey要素

IdPがクライアント・アプリケーション(またはSP)に対してすべてのリクエストに署名することを要求する場合や、IdPがアサーションを暗号化する場合は、それを行うために使用する鍵を定義する必要があります。クライアント署名ドキュメントの場合、署名に使用される秘密鍵と公開鍵、または証明書の両方を定義する必要があります。暗号化の場合、復号化に使用される秘密鍵を定義するだけで済みます。

鍵を記述する方法は2つあります。Javaキーストア内に格納するか、PEM 形式で keycloak-saml.xml 内に直接コピー/ペーストできます。

        <Keys>
            <Key signing="true" >
               ...
            </Key>
        </Keys>

Key 要素には、2つの省略可能な属性 signingencryption があります。 これらをtrueに設定すると、アダプターはどの鍵を使用するか決定します。 両方の属性がtrueに設定される場合は、ドキュメントの署名と暗号化されたアサーションの復号化の両方に鍵が使用されます。これらの属性の少なくとも1つはtrueに設定する必要があります。

KeyStore要素

Key 要素内でJavaのキーストアから鍵と証明書を読み込むことができます。これは KeyStore 要素内で宣言されています。

        <Keys>
            <Key signing="true" >
                <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
                    <PrivateKey alias="myPrivate" password="test123"/>
                    <Certificate alias="myCertAlias"/>
                </KeyStore>
            </Key>
        </Keys>

KeyStore 要素に定義されているXML設定属性を示します。

file

キーストアのファイルパスです。このオプションは OPTIONAL です。file属性またはresource属性を設定する必要があります。

resource

キーストアへのWARリソース・パスです。これは、ServletContext.getResourceAsStream()へのメソッド呼び出しで使用されるパスです。このオプションは OPTIONAL です。file属性またはresource属性を設定する必要があります。

password

キーストアのパスワードです。このオプションは REQUIRED です。

SPがドキュメントの署名に使用する鍵を定義している場合、Javaのキーストア内の秘密鍵と証明書への参照も指定する必要があります。上記の例の PrivateKey 要素と Certificate 要素はキーストア内の鍵または証明書を指す alias を定義します。キーストアには、秘密鍵にアクセスするための追加のパスワードが必要です。 PrivateKey 要素では、このパスワードを password 属性の中で定義する必要があります。

Key PEMS

Key 要素の中では、 PrivateKeyPemPublicKeyPemCertificatePem というサブ要素を使って、鍵と証明書を直接宣言します。これらの要素に含まれる値は、PEM鍵形式に準拠している必要があります。 openssl などのコマンドライン・ツールを使用して鍵を生成する場合は、通常このオプションを使用します。

<Keys>
   <Key signing="true">
      <PrivateKeyPem>
         2341251234AB31234==231BB998311222423522334
      </PrivateKeyPem>
      <CertificatePem>
         211111341251234AB31234==231BB998311222423522334
      </CertificatePem>
   </Key>
</Keys>
SP PrincipalNameMapping要素

この要素はオプションです。 HttpServletRequest.getUserPrincipal() などのメソッドから取得したJavaの Principal オブジェクトを作成するときに、 Principal.getName() メソッドが返す名前を定義することができます。

<SP ...>
  <PrincipalNameMapping policy="FROM_NAME_ID"/>
</SP>

<SP ...>
  <PrincipalNameMapping policy="FROM_ATTRIBUTE" attribute="email" />
</SP>

policy 属性は、この値を設定するためのポリシーを定義します。 この属性に指定できる値は次のとおりです。

FROM_NAME_ID

このポリシーはSAMLサブジェクトの値が何であれ使用します。これはデフォルト設定です。

FROM_ATTRIBUTE

これにより、サーバーから受け取ったSAMLアサーションで宣言された属性の1つから、値が取得されます。 attribute XML属性内で使用するSAMLアサーション属性の名前を指定する必要があります。

RoleIdentifiers要素

RoleIdentifiers 要素は、ユーザーから受信したアサーション内のどのSAML属性が、Java EEのセキュリティー・コンテキスト内のロール識別子として使用されるか定義します。

<RoleIdentifiers>
     <Attribute name="Role"/>
     <Attribute name="member"/>
     <Attribute name="memberOf"/>
</RoleIdentifiers>

デフォルトでは、 Role 属性の値はJava EEのロールに変換されます。いくつかのIdPは、 member または memberOf 属性アサーションを使用してロールを送信します。どのSAML属性をロールに変換すべきかを指定するために、1つ以上の Attribute 要素を定義できます。

RoleMappingsProvider要素

RoleMappingsProvider は、SAMLアダプターによって使用される org.keycloak.adapters.saml.RoleMappingsProvider SPIの実装のidと設定を指定できるオプションの要素です。

KeycloakをIDPとして使用する場合、組み込みのロールマッパーを使用して、ロールをSAMLアサーションに追加する前にマッピングできます。ただし、SAMLアダプターを使用することでSAMLリクエストをサードパーティーのIDPに送信できるため、この場合、SPの要求に応じて、アサーションから抽出されたロールを異なるロールセットにマッピングする必要があります。 RoleMappingsProvider SPIは、必要なマッピングを実行するために使用できるプラグ可能なロールマッパーの設定を可能にします。

プロバイダーの設定は次のようになります。

...
<RoleIdentifiers>
    ...
</RoleIdentifiers>
<RoleMappingsProvider id="properties-based-role-mapper">
    <Property name="properties.resource.location" value="/WEB-INF/role-mappings.properties"/>
</RoleMappingsProvider>
<IDP>
    ...
</IDP>

id 属性は、インストールされているプロバイダーのどれを使用するかを識別します。 Property サブ要素を複数回使用して、プロバイダーの設定プロパティーを指定できます。

プロパティー・ベースのロールマッピング・プロバイダー

Keycloakには、 properties ファイルを使用してロールマッピングを実行する RoleMappingsProvider の実装が含まれます。このプロバイダーは、ID properties-based-role-mapper によって識別され、 org.keycloak.adapters.saml.PropertiesBasedRoleMapper クラスによって実装されます。

このプロバイダーは、使用される properties ファイルの場所を指定するために使用できる2つの設定プロパティーに依存しています。まず、 properties.file.location プロパティーが指定されているかどうかを確認し、設定された値を使用してファイルシステム内の properties ファイルを見つけます。設定されたファイルが見つからない場合、プロバイダーは RuntimeException をスローします。次のスニペットは、 properties.file.configuration オプションを使用して、ファイルシステムの /opt /mappers/ ディレクトリーから roles.properties ファイルをロードするプロバイダーの例を示しています。

    <RoleMappingsProvider id="properties-based-role-mapper">
        <Property name="properties.file.location" value="/opt/mappers/roles.properties"/>
    </RoleMappingsProvider>

properties.file.location が設定されていない場合、プロバイダーは properties.resource.location プロパティーをチェックし、設定された値を使用して WAR リソースから properties ファイルをロードします。この設定プロパティーが存在しない場合、プロバイダーはデフォルトで /WEB-INF/role-mappings.properties からファイルをロードしようとします。リソースからファイルをロードできないと、プロバイダーは RuntimeException をスローします。 次のスニペットは、 properties.resource.location を使用してアプリケーションの /WEB-INF/conf/ ディレクトリーから roles.properties ファイルをロードするプロバイダーの例を示しています。

    <RoleMappingsProvider id="properties-based-role-mapper">
        <Property name="properties.resource.location" value="/WEB-INF/conf/roles.properties"/>
    </RoleMappingsProvider>

properties ファイルには、キーとしてロールとプリンシパルの両方を、値としてカンマで区切られたゼロ以上のロールのリストを含めることができます。呼び出されると、実装はアサーションから抽出された一連のロールを反復処理し、各ロールについてマッピングが存在するかどうかを確認します。ロールが空のロールにマップされている場合は、破棄されます。1つ以上の異なるロールのセットにマップされる場合、これらのロールは結果セットに設定されます。ロールのマッピングが見つからない場合は、結果セットにそのまま含まれます。

ロールが処理されると、実装はアサーションから抽出されたプリンシパルにエントリーの properties ファイルが含まれているかどうかを確認します。プリンシパルのマッピングが存在する場合、値としてリストされているロールが結果セットに追加されます。これにより、プリンシパルに追加のロールを割り当てることができます。

例として、プロバイダーが次のプロパティー・ファイルで設定されていると仮定します。

roleA=roleX,roleY
roleB=

kc_user=roleZ

プリンシパル kc_user がロール roleAroleB および roleC を持つアサーションから抽出される場合、プリンシパルに割り当てられるロールの最終セットは roleCroleXroleY および roleZ になります。 roleAroleXroleY の両方にマップされ、 roleB は空のロールにマップされているので破棄され、 roleC はそのまま使用され、最後に追加のロールが kc_user プリンシパル( roleZ )に追加されるためです。

独自のロールマッピング・プロバイダーの追加

カスタム・ロールマッピング・プロバイダーを追加するには、単に org.keycloak.adapters.saml.RoleMappingsProvider SPIを実装する必要があります。詳細については、 Server Developer GuideSAML Role Mappings SPI のセクションを参照してください。

IDP要素

IDP要素内には、SPと通信しているアイデンティティー・プロバイダー(認証サーバー)の設定を記述します。

<IDP entityID="idp"
     signaturesRequired="true"
     signatureAlgorithm="RSA_SHA1"
     signatureCanonicalizationMethod="http://www.w3.org/2001/10/xml-exc-c14n#">
...
</IDP>

IDP 要素宣言内で指定できる属性設定オプションを示します。

entityID

IDPの発行者IDです。この設定は REQUIRED です。

signaturesRequired

true に設定すると、クライアント・アダプターはIDPに送信する全ての文書に署名します。 また、クライアントは、送信されたすべてのドキュメントにIDPが署名していることを期待します。このスイッチは、すべてのリクエストとレスポンスの種類に対してデフォルト値を設定しますが、それ以上に細かい制御ができることが後で分かります。この設定は OPTIONAL で、 false がデフォルトとなります。

signatureAlgorithm

IDPが署名済みドキュメントに使用することを想定している署名アルゴリズムです。許可値は、 RSA_SHA1RSA_SHA256RSA_SHA512DSA_SHA1 です。 この設定は OPTIONAL で、デフォルトは RSA_SHA256 です。

signatureCanonicalizationMethod

IDPが署名済みのドキュメントに使用することを想定している署名の正規化方法です。この設定は OPTIONAL です。デフォルトは http://www.w3.org/2001/10/xml-exc-c14n# で、ほとんどのIDPに対して変更が不要です。

metadataUrl

IDPメタデータを取得するために使用されるURL。現在、これは定期的に署名および暗号化鍵を取得するために使用されます。これにより、SP側を手動で変更することなく、IDPでこれらの鍵を循環できます。

IDP AllowedClockSkewサブ要素

オプションのサブ要素 AllowedClockSkew は、IDPとSPの間で許可されるクロックスキューを定義します。デフォルト値は0です。

<AllowedClockSkew unit="MILLISECONDS">3500</AllowedClockSkew>
unit

この要素の値に付加される時間単位を定義することができます。許可される値は、MICROSECONDS、MILLISECONDS、MINUTES、NANOSECONDSおよびSECONDSです。これは _OPTIONAL_です。デフォルト値は SECONDS です。

IDP SingleSignOnServiceサブ要素

SingleSignOnService サブ要素は、IDPのログインSAMLエンドポイントを定義します。クライアント・アダプターは、ログインしたいときに、この要素内の設定によって書式設定されたIDPにリクエストを送信します。

<SingleSignOnService signRequest="true"
                     validateResponseSignature="true"
                     requestBinding="post"
                     bindingUrl="url"/>

この要素に定義できる設定属性は次のとおりです。

signRequest

クライアントが認証リクエストに署名する必要があるかどうかの設定です。この設定は OPTIONAL です。デフォルトはIDPの signaturesRequired 要素の値です。

validateResponseSignature

クライアントが、認証リクエストからのアサーション・レスポンスのドキュメントが署名されていることを期待するかどうかの設定です。この設定は OPTIONAL です。デフォルトはIDPの signaturesRequired 要素の値です。

requestBinding

IDPとの通信に使用するSAMLバインディング・タイプです。この設定は OPTIONAL です。デフォルト値は POST ですが、 REDIRECT に設定することもできます。

responseBinding

SAMLでは、クライアントが認証レスポンスに使用したいバインディング・タイプを要求することができます。この値には、 POST または REDIRECT を設定できます。この設定は OPTIONAL です。デフォルトでは、クライアントはレスポンスに対して特定のバインディング・タイプを要求しません。

assertionConsumerServiceUrl

IDPログイン・サービスがレスポンスを送信するアサーション・コンシューマー・サービス(ACS)のURLの設定です。この設定は OPTIONAL です。デフォルトでは未設定で、IdPの設定に依存します。設定されている場合、 http://sp.domain.com/my/endpoint/for/saml のように、 /saml で終わる必要があります。このプロパティーの値は、SAMLの AuthnRequest メッセージの AssertionConsumerServiceURL 属性で送信されます。このプロパティーは、通常 responseBinding 属性を伴います。

bindingUrl

これは、クライアントがリクエストを送信するIDPログイン・サービスのURLです。この設定は REQUIRED です。

IDP SingleLogoutServiceサブ要素

SingleLogoutService サブ要素は、IDPのログアウトSAMLエンドポイントを定義します。クライアント・アダプターは、ログアウトしたいときに、この要素内の設定によって書式設定されたIDPにリクエストを送信します。

<SingleLogoutService validateRequestSignature="true"
                     validateResponseSignature="true"
                     signRequest="true"
                     signResponse="true"
                     requestBinding="redirect"
                     responseBinding="post"
                     postBindingUrl="posturl"
                     redirectBindingUrl="redirecturl">
signRequest

IDPへのログアウト・リクエストにクライアントが署名する必要があるかどうかの設定です。この設定は OPTIONAL です。デフォルトはIDPの signaturesRequired 要素の値です。

signResponse

IDPへのログアウト・レスポンスにクライアントが署名する必要があるかどうかの設定です。この設定は OPTIONAL です。デフォルトはIDPの signaturesRequired 要素の値です。

validateRequestSignature

IDPからのログアウト・リクエストのドキュメントが署名されていることをクライアントが期待するかどうかの設定です。この設定は OPTIONAL です。デフォルトはIDPの signaturesRequired 要素の値です。

validateResponseSignature

IDPからのログアウト・レスポンスのドキュメントが署名されていることをクライアントが期待するかどうかの設定です。この設定は OPTIONAL です。デフォルトはIDPの signaturesRequired 要素の値です。

requestBinding

IDPとのSAMLリクエストでの通信に使用するSAMLバインディング・タイプです。この設定は OPTIONAL です。デフォルト値は POST ですが、 REDIRECT に設定することもできます。

responseBinding

IDPとのSAMLレスポンスでの通信に使用するSAMLバインディング・タイプです。この値は POST または REDIRECT にできます。この設定は OPTIONAL です。デフォルト値は POST ですが、 REDIRECT に設定することもできます。

postBindingUrl

POST バインディングを使用する際のIDPのログアウト・サービス用のURLです。 POST バインディングを使用する場合、この設定は REQUIRED です。

redirectBindingUrl

REDIRECTバインディングを使用する際のIDPのログアウト・サービス用のURLです。REDIRECTバインディングを使用する場合、この設定は REQUIRED です。

IDP Keysサブ要素

IDPのKeysサブ要素は、IDPによって署名されたドキュメントの検証に使用する証明書や公開鍵を定義するために使用されます。Keysサブ要素は、SPのKeys要素と同じ方法で定義されます。しかし、証明書または公開鍵の参照を1つだけ定義する必要があります。IDPとSPがKeycloakサーバーとアダプターによって実現された場合、それぞれで、署名の検証のために鍵を指定する必要はないことに注意してください(以下を参照してください)。

SPとIDPの両方がKeycloakで提供された場合、公開された証明書から自動的にIDP署名検証用の公開鍵を取得するようにSPを設定できます。これは、Keysサブ要素内の署名検証鍵のすべての宣言を削除することによって行われます。Keysサブ要素が空であれば、完全に省略できます。鍵は、SAML記述子からSPによって自動的に取得されます。その場所は、IDP SingleSignOnServiceサブ要素で指定したSAMLエンドポイントURLが基になります。SAML記述子の取得に使用されるHTTPクライアントの設定は、通常追加の設定は必要がありませんが、IDP HttpClientサブ要素内に設定できます。

署名検証用の鍵を複数指定することもできます。 signing 属性を true に設定し、Keysサブ要素内に複数のKey要素を宣言することによって行います。これは、たとえばIDPの署名鍵をローテーションするような状況で役に立ちます。通常、新しいSAMLプロトコルのメッセージとアサーションが新しい鍵で署名されても、以前の鍵で署名されたものを受け入れられる移行期間があります。

署名検証用の鍵を自動的に取得することと、追加の静的な署名検証の鍵を定義することの、両方をKeycloakに設定することはできません。

       <IDP entityID="idp">
            ...
            <Keys>
                <Key signing="true">
                    <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
                        <Certificate alias="demo"/>
                    </KeyStore>
                </Key>
            </Keys>
        </IDP>
IDP HttpClient サブ要素

オプションである HttpClient サブ要素は、HTTPクライアントのプロパティーを定義します。enabledの場合、IDPのSAML記述子による、IDP署名検証用の公開鍵を含む証明書の自動取得に使用されるます。

<HttpClient connectionPoolSize="10"
            disableTrustManager="false"
            allowAnyHostname="false"
            clientKeystore="classpath:keystore.jks"
            clientKeystorePassword="pwd"
            truststore="classpath:truststore.jks"
            truststorePassword="pwd"
            proxyUrl="http://proxy/" />
connectionPoolSize

この設定オプションは、Keycloakサーバーにプールすべき接続数を定義します。これは OPTIONAL です。デフォルトは 10 です。

disableTrustManager

KeycloakサーバーがHTTPSを必要とし、この設定オプションを true に設定する場合は、トラストストアを指定する必要はありません。この設定は、SSL証明書の検証を無効とするため、開発時にのみ使用すべきで、プロダクション環境では 決して使用してはいけません 。これは OPTIONAL です。デフォルトは false です。

allowAnyHostname

KeycloakサーバーがHTTPSを必要とし、この設定オプションを true に設定する場合、トラストストア経由でKeycloakサーバーの証明書は検証されますが、ホスト名の検証は行われません。 SSL証明書の検証が一部無効となるため、この設定は開発時にのみ使用すべきで、プロダクション環境では 決して使用してはいけません 。これは OPTIONAL です。デフォルトは false です。

truststore

トラストストア・ファイルへのファイルパスです。パスに classpath: を付けると、トラストストアはデプロイメントのクラスパスから取得されます。これはKeycloakサーバーへのHTTPS通信に使用されます。HTTPSリクエストを行うクライアントは、通信を行うサーバーのホストを検証する必要があります。これがトラストストアの役割です。キーストアには、1つ以上の信頼されたホストの証明書または認証局が含まれています。このトラストストアは、KeycloakサーバーのSSLキーストアのパブリック証明書を抽出することで作成できます。これは、 disableTrustManagertrue でない限り、 REQUIRED です。

truststorePassword

トラストストアのパスワードです。これは REQUIRED で、 truststore を設定すると、トラストストアはパスワードを必要とします。

clientKeystore

キーストア・ファイルへのファイルパスです。このキーストアには、アダプターがKeycloakサーバーにHTTPSリクエストを行う際の、双方向SSLのためのクライアント証明書を含めます。これは OPTIONAL です。

clientKeystorePassword

クライアントのキーとクライアントのキーストアのパスワードです。 clientKeystore が設定されている場合は、 REQUIRED です。

proxyUrl

HTTP接続に使用するHTTPプロキシーのURLです。これは OPTIONAL です。

3.1.2. JBoss EAP/WildFlyアダプター

JBoss EAPまたはWildFlyにデプロイされたWARアプリケーションを保護するには、Keycloak SAMLアダプター・サブシステムをインストールして設定する必要があります。

keycloak設定ファイルである /WEB-INF/keycloak-saml.xml をWARに提供し、 web.xml 内の auth-methodKEYCLOAK-SAML に変更します。両方の方法についてこのセクションで説明します。

アダプターのインストール

各アダプターは、Keycloakのダウンロード・サイトで個別にダウンロードできます。

今回のリリースでは、WildFlyの最新バージョンでのみアダプターのテストとメンテナンスを行っています。WildFlyの新しいバージョンがリリースされると、現在のアダプターは非推奨になり、WildFlyの次のリリース後にサポートが削除されます。もう一つの選択肢は、JBoss EAPアダプターの場合はより長期間サポートされるので、アプリケーションをWildFlyからJBoss EAPに切り替えることです。

WildFly 9以上、またはJBoss EAP 7へのインストールは次のとおりです。

$ cd $WILDFLY_HOME
$ unzip keycloak-saml-wildfly-adapter-dist.zip

JBoss EAP 6.xへのインストールは次のとおりです。

$ cd $JBOSS_HOME
$ unzip keycloak-saml-eap6-adapter-dist.zip

これらのzipファイルは、WildFlyまたはJBoss EAP用の配布物内のWildFly/JBoss EAP SAMLアダプターに固有の新しいJBossモジュールを作成します。

モジュールを追加したら、アプリケーション・サーバーのサーバー設定 domain.xml または standalone.xml 内にあるKeycloak SAMLサブシステムを有効にする必要があります。

サーバー設定の変更に役立つCLIスクリプトがあります。サーバーを起動し、サーバーの bin ディレクトリーからスクリプトを実行します。

WildFly 11以上
$ cd $JBOSS_HOME
$ ./bin/jboss-cli.sh -c --file=bin/adapter-elytron-install-saml.cli
WildFly 10以下
$ cd $JBOSS_HOME
$ /bin/boss-cli.sh -c --file=bin/adapter-install-saml.cli
WildFly 11以上でもレガシーな非Elytronアダプターが使用できます。つまり、それらのバージョンでも adapter-install-saml.cli を使用することができます。ただし、新しいElytronアダプターを使用することをお勧めします。

スクリプトは、以下に説明するように、extension、subsystem、オプションでsecurity-domainを追加します。

<server xmlns="urn:jboss:domain:1.4">

    <extensions>
        <extension module="org.keycloak.keycloak-saml-adapter-subsystem"/>
          ...
    </extensions>

    <profile>
        <subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1"/>
         ...
    </profile>

セキュリティー保護されたWeb層で作成されたセキュリティー・コンテキストを呼び出すEJB(他のEEコンポーネント)に伝播する必要がある場合は、 keycloak セキュリティー・ドメインをEJBなどのコンポーネントとともに使用する必要があります。それ以外の場合、この設定はオプションです。

<server xmlns="urn:jboss:domain:1.4">
 <subsystem xmlns="urn:jboss:domain:security:1.2">
    <security-domains>
...
      <security-domain name="keycloak">
         <authentication>
           <login-module code="org.keycloak.adapters.jboss.KeycloakLoginModule"
                         flag="required"/>
          </authentication>
      </security-domain>
    </security-domains>

セキュリティー・コンテキストは、EJB層に自動的に伝播されます。

JBoss SSO

WildFlyには、同じWildFlyインスタンスにデプロイされたWebアプリケーションのシングル・サインオンのサポートが組み込まれています。これは、Keycloakを使用しているときは有効にしないでください。

JSESSIONID CookieにSameSite値を設定する

各ブラウザーは、Cookieの SameSite 属性のデフォルト値を Lax に設定することを計画しています。この設定は、リクエストが同じドメインで発生した場合にのみCookieがアプリケーションに送信されることを意味します。この動作はSAML POSTバインディングに影響を与え、機能しなくなる可能性があります。SAMLアダプターの完全な機能を保持するために、コンテナーによって作成された JSESSIONID Cookieの SameSite 値を None に設定することをお勧めします。そうしないと、Keycloakへのリクエストごとにコンテナーのセッションがリセットされる可能性があります。

SameSite 属性が None に設定されないようにするには、許容できる場合はREDIRECTバインディングに切り替えるか、この回避策が不要な場合はOIDCプロトコルに切り替えることを検討してください。

Wildfly/EAPの JSESSIONID Cookieの SameSite 値を None に設定するには、以下の内容のファイル undertow-handlers.conf をアプリケーションの WEB-INF ディレクトリーに追加します。

samesite-cookie(mode=None, cookie-pattern=JSESSIONID)

この設定のサポートは、バージョン19.1.0以降のWildFlyで利用できます。

3.1.3. RPMからのJBoss EAPアダプターのインストール

次のように、RPMからEAP 7アダプターをインストールします。

Red Hat Enterprise Linux 7では、チャンネルという用語はリポジトリーという用語に置き換えられました。これらの説明では、リポジトリーという用語のみが使用されています。

RPMからEAP 7アダプターをインストールする前に、JBoss EAP 7リポジトリーにサブスクライブする必要があります。

前提条件
  1. Red Hat Subscription Managerを使用して、Red Hat Enterprise Linuxシステムがアカウントに登録されていることを確認してください。詳細は、Red Hat Subscription Management documentationのリンクを参照してください。

  2. すでに別のJBoss EAPリポジトリーに登録している場合は、まずそのリポジトリーから登録を解除する必要があります。

Red Hat Enterprise Linux 6、7の場合:Red Hat Subscription Managerを使用して、次のコマンドでWildFly 21リポジトリーに登録します。Red Hat Enterprise Linuxバージョンに応じて、 <RHEL_VERSION> を6または7のいずれかに置き換えてください。

$ sudo subscription-manager repos --enable=jb-eap-7-for-rhel-<RHEL_VERSION>-server-rpms

Red Hat Enterprise Linux 8の場合:Red Hat Subscription Managerを使用して、次のコマンドでWildFly 21リポジトリーに登録します。

$ sudo subscription-manager repos --enable=jb-eap-21-for-rhel-8-x86_64-rpms --enable=rhel-8-for-x86_64-baseos-rpms --enable=rhel-8-for-x86_64-appstream-rpms

次のコマンドを使用して、SAML用のEAP 7アダプターをインストールします。

$ sudo yum install eap7-keycloak-saml-adapter-sso7_4

または、次のいずれかをRed Hat Enterprise Linux 8に使用します。

$ sudo dnf install eap7-keycloak-adapter-sso7_4
RPMインストールのためのデフォルトのEAP_HOMEパスは、/opt/rh/eap7/root/usr/share/wildflyです。

適切なモジュール・インストール・スクリプトを実行します。

SAMLモジュールの場合は、次のコマンドを実行します。

$ $EAP_HOME/bin/jboss-cli.sh -c --file=$EAP_HOME/bin/adapter-install-saml.cli

インストールは完了です。

次のように、RPMからEAP 6アダプターをインストールします。

Red Hat Enterprise Linux 7では、チャンネルという用語はリポジトリーという用語に置き換えられました。これらの説明では、リポジトリーという用語のみが使用されています。

RPMからEAP 6アダプターをインストールする前に、JBoss EAP 6リポジトリーにサブスクライブする必要があります。

前提条件
  1. Red Hat Subscription Managerを使用して、Red Hat Enterprise Linuxシステムがアカウントに登録されていることを確認してください。詳細は、Red Hat Subscription Management documentationのリンクを参照してください。

  2. すでに別のJBoss EAPリポジトリーに登録している場合は、まずそのリポジトリーから登録を解除する必要があります。

Red Hat Subscription Managerを使用して、次のコマンドを使用してJBoss EAP 6リポジトリーにサブスクライブします。Red Hat Enterprise Linuxのバージョンに応じて、<RHEL_VERSION>を6または7のいずれかに置き換えてください。

$ sudo subscription-manager repos --enable=jb-eap-6-for-rhel-<RHEL_VERSION>-server-rpms

次のコマンドを使用して、SAML用のEAP 6アダプターをインストールします。

$ sudo yum install keycloak-saml-adapter-sso7_4-eap6
RPMインストールのためのデフォルトのEAP_HOMEパスは、/opt/rh/eap6/root/usr/share/wildflyです。

適切なモジュール・インストール・スクリプトを実行します。

SAMLモジュールの場合は、次のコマンドを実行します。

$ $EAP_HOME/bin/jboss-cli.sh -c --file=$EAP_HOME/bin/adapter-install-saml.cli

インストールは完了です。

WARごとの設定

このセクションでは、直接WARパッケージ内に設定を追加し、ファイルを編集することで、WARをセキュリティー保護する方法について説明します。

まず、WARの WEB-INF ディレクトリーに keycloak-saml.xml アダプター設定ファイルを作成します。この設定ファイルの形式は、共通アダプター設定セクションで説明しています。

次に、 web.xmlKEYCLOAK-SAMLauth-method を設定する必要があります。また、標準サーブレット・セキュリティーを使用して、URLに対してロールベース制約を指定する必要があります。 web.xml ファイルの例を次に示します。

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      version="3.0">

        <module-name>customer-portal</module-name>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Admins</web-resource-name>
            <url-pattern>/admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>admin</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Customers</web-resource-name>
            <url-pattern>/customers/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>user</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>

    <login-config>
        <auth-method>KEYCLOAK-SAML</auth-method>
        <realm-name>this is ignored currently</realm-name>
    </login-config>

    <security-role>
        <role-name>admin</role-name>
    </security-role>
    <security-role>
        <role-name>user</role-name>
    </security-role>
</web-app>

auth-method の設定を除いて、すべての標準のサーブレット設定です。

Keycloak SAMLサブシステムを介してWARをセキュリティー保護する

Keycloakでセキュリティー保護するためにWARを開く必要はありません。代わりに、Keycloak SAMLアダプター・サブシステムを使用して、外部からセキュリティー保護することもできます。KEYCLOAK-SAMLを auth-method として指定する必要はありませんが、 web.xmlsecurity-constraints を定義する必要があります。しかし、 WEB-INF/keycloak-saml.xml ファイルを作成する必要はありません。このメタデータは、代わりにサーバーの domain.xml または standalone.xml のサブシステム設定セクションのXML内で定義されます。

<extensions>
  <extension module="org.keycloak.keycloak-saml-adapter-subsystem"/>
</extensions>

<profile>
  <subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1">
    <secure-deployment name="WAR MODULE NAME.war">
      <SP entityID="APPLICATION URL">
        ...
      </SP>
    </secure-deployment>
  </subsystem>
</profile>

secure-deploymentname 属性は保護したいWARを識別します。 web.xml 内の module-name.war を付加した値となります。残りの設定は、共通アダプター設定で定義された keycloak-saml.xml 設定と同じXML構文を使用します。

設定例を次に示します。

<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1">
  <secure-deployment name="saml-post-encryption.war">
    <SP entityID="http://localhost:8080/sales-post-enc/"
        sslPolicy="EXTERNAL"
        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
        logoutPage="/logout.jsp"
        forceAuthentication="false">
      <Keys>
        <Key signing="true" encryption="true">
          <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
            <PrivateKey alias="http://localhost:8080/sales-post-enc/" password="test123"/>
            <Certificate alias="http://localhost:8080/sales-post-enc/"/>
          </KeyStore>
        </Key>
      </Keys>
      <PrincipalNameMapping policy="FROM_NAME_ID"/>
      <RoleIdentifiers>
        <Attribute name="Role"/>
      </RoleIdentifiers>
      <IDP entityID="idp">
        <SingleSignOnService signRequest="true"
            validateResponseSignature="true"
            requestBinding="POST"
            bindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"/>

        <SingleLogoutService
            validateRequestSignature="true"
            validateResponseSignature="true"
            signRequest="true"
            signResponse="true"
            requestBinding="POST"
            responseBinding="POST"
            postBindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"
            redirectBindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"/>
        <Keys>
          <Key signing="true" >
            <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
              <Certificate alias="saml-demo"/>
            </KeyStore>
          </Key>
        </Keys>
      </IDP>
    </SP>
   </secure-deployment>
</subsystem>

3.1.4. Tomcat SAMLアダプター

Tomcat 7、8、9にデプロイされたWARアプリケーションを保護するには、Keycloak Tomcat 7 SAMLアダプターまたはKeycloak Tomcat SAMLアダプターをTomcatにインストールする必要があります。その後、TomcatにデプロイするWARにもいくつかの設定を行う必要があります。これらの手順について説明します。

アダプターのインストール

アダプターはアプライアンスやwarには含まれていません。各アダプターは、Keycloakのダウンロード・サイトで個別にダウンロードできます。これらは、mavenのアーティファクトとしても利用できます。

アダプターの配布物をTomcatの lib/ ディレクトリーに解凍する必要があります。WEB-INF/libディレクトリー内にアダプターのjarを含めても動作しません!Keycloak SAMLアダプターはValveとして実装され、ValveのコードはTomcatのメインのlib/ディレクトリーに存在する必要があります。

Tomcat 7へのインストールは次のとおりです。

$ cd $TOMCAT_HOME/lib
$ unzip keycloak-saml-tomcat7-adapter-dist.zip

Tomcat 8、9へのインストールは次のとおりです。

$ cd $TOMCAT_HOME/lib
$ unzip keycloak-saml-tomcat-adapter-dist.zip
WARごとの設定

このセクションでは、直接WARパッケージ内に設定を追加し、ファイルを編集することで、WARをセキュリティー保護する方法について説明します。

まず、WARパッケージに META-INF/context.xml ファイルを作成します。 これはTomcat固有の設定ファイルであり、Keycloak固有のValveを定義する必要があります。

<Context path="/your-context-path">
    <Valve className="org.keycloak.adapters.saml.tomcat.SamlAuthenticatorValve"/>
</Context>

次に、WARの WEB-INF ディレクトリーに keycloak-saml.xml アダプター設定ファイルを作成する必要があります。この設定ファイルの形式は、共通アダプター設定のセクションで説明しています。

最後に、URLに対してロールベース制約を指定するために、 login-config と標準のサーブレット・セキュリティーの両方を指定する必要があります。例を次に示します。

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      version="3.0">

        <module-name>customer-portal</module-name>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Customers</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>user</role-name>
        </auth-constraint>
    </security-constraint>

    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>this is ignored currently</realm-name>
    </login-config>

    <security-role>
        <role-name>admin</role-name>
    </security-role>
    <security-role>
        <role-name>user</role-name>
    </security-role>
</web-app>

keycloak-saml.xml が明示的に assertionConsumerServiceUrl を設定しない場合、SAMLアダプターは暗黙的に /my-context-path/saml の場所でSAMLアサーションをリッスンします。これは、IDPのレルム/クライアント設定の Master SAML Processing URL と一致しなければなりません。例えば、 http://sp.domain.com/my-context-path/saml 。そうでない場合、Tomcatはおそらくユーザーがログインした後にSAMLアサーションを受信しないため、IDPログインサービスに無限にリダイレクトします。

JSESSIONID CookieにSameSite値を設定する

各ブラウザーは、Cookieの SameSite 属性のデフォルト値を Lax に設定することを計画しています。この設定は、リクエストが同じドメインで発生した場合にのみCookieがアプリケーションに送信されることを意味します。この動作はSAML POSTバインディングに影響を与え、機能しなくなる可能性があります。SAMLアダプターの完全な機能を保持するために、コンテナーによって作成された JSESSIONID Cookieの SameSite 値を None に設定することをお勧めします。そうしないと、Keycloakへのリクエストごとにコンテナーのセッションがリセットされる可能性があります。

SameSite 属性が None に設定されないようにするには、許容できる場合はREDIRECTバインディングに切り替えるか、この回避策が不要な場合はOIDCプロトコルに切り替えることを検討してください。

Tomcatの JSESSIONID Cookieの SameSite 値を None に設定するには、アプリケーションの context.xml に次の設定を追加します。これにより、Tomcatコンテナーによって作成されたすべてのCookieの SameSite 値が None に設定されることに注意してください。

<CookieProcessor sameSiteCookies="None" />
SameSite 属性をCookieのサブセットのみに設定することはできません。そのため、アプリケーション用に作成されたすべてのCookieでは、この属性が None に設定されます。

この機能のサポートは、バージョン9.0.29および8.5.49のTomcatで利用できます。

3.1.5. Jetty SAMLアダプター

JettyにデプロイされたWARアプリケーションを保護するには、Keycloak Jetty 9.x SAMLアダプターをJettyにインストールする必要があります。その後、JettyにデプロイするWARにもいくつかの設定を行う必要があります。これらの手順について説明します。

Jetty 9アダプターのインストール

KeycloakにはJetty 9.x用のSAMLアダプターがあります。Jettyにデプロイする各WARには、さらにいくつかの設定を行う必要があります。これらの手順について説明します。

アダプターはアプライアンスやwarには含まれていません。各アダプターは、Keycloakのダウンロード・サイトで個別にダウンロードできます。これらは、mavenのアーティファクトとしても利用できます。

Jetty 9.x用の配布物をJetty 9.xのルート・ディレクトリーに解凍する必要があります。WEB-INF/libディレクトリー内にアダプターのjarを含めても動作しません!

$ cd $JETTY_HOME
$ unzip keycloak-saml-jetty92-adapter-dist.zip

次に、jetty.base用のkeycloakモジュールを有効にする必要があります。

$ cd your-base
$ java -jar $JETTY_HOME/start.jar --add-to-startd=keycloak
WARごとのJetty 9の設定

このセクションでは、直接WARパッケージ内に設定を追加し、ファイルを編集することで、WARをセキュリティー保護する方法について説明します。

まず、WARパッケージに WEB-INF/jetty-web.xml ファイルを作成します。これはJetty固有の設定ファイルで、その中にKeycloak固有のAuthenticatorを定義する必要があります。

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
    <Get name="securityHandler">
        <Set name="authenticator">
            <New class="org.keycloak.adapters.saml.jetty.KeycloakSamlAuthenticator">
            </New>
        </Set>
    </Get>
</Configure>

次に、WARの WEB-INF ディレクトリーに keycloak-saml.xml アダプター設定ファイルを作成する必要があります。この設定ファイルの形式は、共通アダプター設定のセクションで説明しています。

最後に、URLに対してロールベース制約を指定するために、 login-config と標準のサーブレット・セキュリティーの両方を指定する必要があります。例を次に示します。

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      version="3.0">

        <module-name>customer-portal</module-name>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Customers</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>user</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>

    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>this is ignored currently</realm-name>
    </login-config>

    <security-role>
        <role-name>admin</role-name>
    </security-role>
    <security-role>
        <role-name>user</role-name>
    </security-role>
</web-app>

3.1.6. Javaサーブレット・フィルター・アダプター

サーブレット・プラットフォーム用のアダプターを持たないJavaサーブレット・アプリケーションでSAMLを使用する場合は、Keycloakにあるサーブレット・フィルター・アダプターを使用することができます。このアダプターは、他のアダプターとは少し異なります。共通アダプター設定セクションで定義されているように /WEB-INF/keycloak-saml.xml ファイルを指定する必要がありますが、 web.xml にセキュリティー制約を定義しません。代わりにKeycloakサーブレット・フィルター・アダプターを使用してフィルター・マッピングを定義して、URLパターンでセキュリティー保護します。

バックチャネル・ログアウトは、標準のアダプターとは少し異なります。 HTTPセッションを無効にするのではなく、セッションIDをログアウトしたものとしてマークします。 セッションIDに基づいてHTTPセッションを任意に無効にする方法はありません。
現時点では、SAMLフィルターを使用するクラスター化されたアプリケーションでは、バックチャネル・ログアウトは機能しません。
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      version="3.0">

        <module-name>customer-portal</module-name>

    <filter>
        <filter-name>Keycloak Filter</filter-name>
        <filter-class>org.keycloak.adapters.saml.servlet.SamlFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>Keycloak Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

Keycloakフィルターは、コンテキスト・パラメータの代わりにフィルター初期化パラメータとして定義する必要がある以外は、他のアダプターと同じ設定パラメータを使用できます。

さまざまな異なる安全なURLパターンと安全でないURLパターンが存在する場合は、複数のフィルター・マッピングを定義できます。

/saml をカバーするフィルター・マッピングが必要です。 このマッピングは、すべてのサーバー・コールバックを対象としています。

IdPとともにSPを登録するときは、Assert Consumer Service URLとSingle Logout Service URLとして http[s]://hostname/{context-root}/saml を登録する必要があります。

To use this filter, include this maven artifact in your WAR poms:

<dependency>
   <groupId>org.keycloak</groupId>
   <artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
   <version>11.0.0</version>
</dependency>

マルチテナンシーを使用するには、 keycloak.config.resolver パラメーターをフィルター・パラメーターとして渡す必要があります。

    <filter>
        <filter-name>Keycloak Filter</filter-name>
        <filter-class>org.keycloak.adapters.saml.servlet.SamlFilter</filter-class>
        <init-param>
            <param-name>keycloak.config.resolver</param-name>
            <param-value>example.SamlMultiTenantResolver</param-value>
        </init-param>
    </filter>

3.1.7. アイデンティティー・プロバイダーでの登録

サーブレット・ベース・アダプターごとに、アサート・コンシューマー・サービスURLとシングル・ログアウト・サービスURLに登録するエンドポイントは、 /saml が付加されるサーブレット・アプリケーションのベースURLでなければなりません。つまり、 https://example.com/contextPath/saml のようなURLになります。

3.1.8. ログアウト

Webアプリケーションからログアウトするには、複数の方法があります。Java EEサーブレット・コンテナーの場合は、 HttpServletRequest.logout() を呼び出すことができます。他のブラウザーアプリケーションでは、ブラウザーにセキュリティー制約のあるWebアプリケーションのURLを指定し、クエリー・パラメーターGLO、つまり、 http://myapp?GLO=true を渡すことができます。これにより、ブラウザーとのSSOセッションがある場合は、ログアウトされます。

クラスター環境でのログアウト

内部的には、SAMLアダプターは、SAMLセッション・インデックス、プリンシパル名(既知の場合)、およびHTTPセッションIDの間のマッピングを保存します。このマッピングは、分散アプリケーション用にJBossアプリケーション・サーバー・ファミリー(Wildfly 10/11、EAP 6/7)のクラスター全体で維持されます。前提条件として、HTTPセッションをクラスター全体に分散する必要があります(つまり、アプリケーションの web.xml<distributable/> タグが設定されています)。

この機能を有効にするには、 /WEB_INF/web.xml ファイルに次のセクションを追加してください。

EAP 7、WildFly 10/11の場合:

<context-param>
    <param-name>keycloak.sessionIdMapperUpdater.classes</param-name>
    <param-value>org.keycloak.adapters.saml.wildfly.infinispan.InfinispanSessionCacheIdMapperUpdater</param-value>
</context-param>

EAP 6の場合:

<context-param>
    <param-name>keycloak.sessionIdMapperUpdater.classes</param-name>
    <param-value>org.keycloak.adapters.saml.jbossweb.infinispan.InfinispanSessionCacheIdMapperUpdater</param-value>
</context-param>

デプロイメントのセッション・キャッシュの名前が deployment-cache である場合、SAMLマッピングに使用されるキャッシュは deployment-cache.ssoCache という名前になります。キャッシュの名前は、コンテキスト・パラメーター keycloak.sessionIdMapperUpdater.infinispan.cacheName によってオーバーライドできます。キャッシュを含むキャッシュ・コンテナーは、デプロイメント・セッション・キャッシュを含むキャッシュ・コンテナーと同じですが、コンテキスト・パラメーター keycloak.sessionIdMapperUpdater.infinispan.containerName によってオーバーライドできます。

デフォルトでは、SAMLマッピング・キャッシュの設定はセッション・キャッシュから取得されます。この設定は、他のキャッシュとまったく同じサーバーのキャッシュ設定セクションで手動でオーバーライドできます。

現在、信頼性の高いサービスを提供するために、SAMLセッション・キャッシュにレプリケート・キャッシュを使用することをお勧めします。分散キャッシュを使用すると、SAMLログアウト・リクエストがSAMLセッション・インデックスからHTTPセッション・マッピングへのアクセスがないノードに到達し、ログアウトに失敗する結果につながる可能性があります。

クロスDCシナリオでのログアウト

クロスDCシナリオは、WildFly 10以上、EAP 7以上にのみ適用されます。

複数のデータセンターにまたがるセッションを処理するには、特別な処理が必要です。以下のシナリオを想像してみてください。

  1. ログイン・リクエストは、データセンター1のクラスター内で処理されます。

  2. 管理者が特定のSAMLセッションのログアウト・リクエストを発行すると、そのリクエストはデータセンター2に送信されます。

データセンター2は、データセンター1(およびHTTPセッションを共有する他のすべてのデータセンター)に存在するすべてのセッションをログアウトする必要があります。

このケースをカバーするために、上記で記述されたSAMLセッション・キャッシュは、個々のクラスター内だけでなく、すべてのデータセンターにわたってレプリケーションする必要があります。例: スタンドアローンInfinispan/JDGサーバー経由

  1. スタンドアローンのInfinispan/JDGサーバーにキャッシュを追加する必要があります。

  2. 前の項目のキャッシュは、それぞれのSAMLセッション・キャッシュ用にリモートストアとして追加する必要があります。

デプロイ中にリモートストアがSAMLセッション・キャッシュに存在すると、変更が監視され、ローカルSAMLセッション・キャッシュがそれに応じて更新されます。

3.1.9. アサーション属性の取得

SAMLログインが成功したら、アプリケーションのコードで、SAMLアサーションに渡された属性値を取得したい場合があるかもしれません。 HttpServletRequest.getUserPrincipal() は、 org.keycloak.adapters.saml.SamlPrincipal と呼ばれるKeycloak特有のクラスにキャスト可能な、 Principal オブジェクトを返します。このオブジェクトには、未加工のアサーションを参照したり、属性値を取得する便利な機能があります。

package org.keycloak.adapters.saml;

public class SamlPrincipal implements Serializable, Principal {
    /**
     * Get full saml assertion
     *
     * @return
     */
    public AssertionType getAssertion() {
       ...
    }

    /**
     * Get SAML subject sent in assertion
     *
     * @return
     */
    public String getSamlSubject() {
        ...
    }

    /**
     * Subject nameID format
     *
     * @return
     */
    public String getNameIDFormat() {
        ...
    }

    @Override
    public String getName() {
        ...
    }

    /**
     * Convenience function that gets Attribute value by attribute name
     *
     * @param name
     * @return
     */
    public List<String> getAttributes(String name) {
        ...

    }

    /**
     * Convenience function that gets Attribute value by attribute friendly name
     *
     * @param friendlyName
     * @return
     */
    public List<String> getFriendlyAttributes(String friendlyName) {
        ...
    }

    /**
     * Convenience function that gets first  value of an attribute by attribute name
     *
     * @param name
     * @return
     */
    public String getAttribute(String name) {
        ...
    }

    /**
     * Convenience function that gets first  value of an attribute by attribute name
     *
     *
     * @param friendlyName
     * @return
     */
    public String getFriendlyAttribute(String friendlyName) {
        ...
    }

    /**
     * Get set of all assertion attribute names
     *
     * @return
     */
    public Set<String> getAttributeNames() {
        ...
    }

    /**
     * Get set of all assertion friendly attribute names
     *
     * @return
     */
    public Set<String> getFriendlyNames() {
        ...
    }
}

3.1.10. エラー処理

Keycloakには、サーブレットベースのクライアント・アダプター用のエラー処理機能があります。認証でエラーが発生すると、クライアント・アダプターは HttpServletResponse.sendError() を呼び出します。 web.xml ファイル内にerror-pageを設定してエラーを処理することができます。クライアント・アダプターは、400、401、403、500のエラーをスローできます。

<error-page>
    <error-code>403</error-code>
    <location>/ErrorHandler</location>
</error-page>

クライアント・アダプターはまた、取得可能な HttpServletRequest 属性を設定します。属性名は、 org.keycloak.adapters.spi.AuthenticationError です。このオブジェクトを org.keycloak.adapters.saml.SamlAuthenticationError にキャストします。このクラスは、正確に何が起こったかを伝えることができます。この属性が設定されない場合、アダプターはエラーコードに対して何もしません。

public class SamlAuthenticationError implements AuthenticationError {
    public static enum Reason {
        EXTRACTION_FAILURE,
        INVALID_SIGNATURE,
        ERROR_STATUS
    }

    public Reason getReason() {
        return reason;
    }
    public StatusResponseType getStatus() {
        return status;
    }
}

3.1.11. トラブルシューティング

問題を解決する最善の方法は、SAMLクライアント・アダプターとKeycloakサーバーの両方でデバッギングを有効にすることです。ロギング・フレームワークを使用して、 org.keycloak.saml パッケージのログ・レベルを DEBUG に設定します。これを有効にすることで、サーバー間で送信されるSAMLのリクエスト/レスポンスのドキュメントを見ることができます。

3.1.12. マルチテナンシー

SAMLは、マルチテナンシー についてOIDCと同じ機能を提供します。つまり、単一のターゲット・アプリケーション(WAR)を複数のKeycloakレルムで保護できます。レルムは、同じKeycloakインスタンスまたは異なるインスタンスに配置できます。

これを行うには、アプリケーションは複数の keycloak-saml.xml アダプター設定ファイルを持つ必要があります。

異なるコンテキストパスに異なるアダプター設定ファイルをデプロイして、WARの複数のインスタンスを作成することができますが、これは不便であり、コンテキストパス以外のものに基づいてレルムを選択することもできます。

Keycloakではカスタム設定リゾルバーを持つことが可能なため、どのアダプター設定が各リクエストに使用されるかを選択することができます。SAMLでは、設定はログイン処理においてのみ関係しています。ユーザーがログインするとセッションは認証され、返される keycloak-saml.xml が異なっていても問題ありません。そのため、同じセッションに対して同じ設定を返すことが正しい方法です。

これを実現するためには、 org.keycloak.adapters.saml.SamlConfigResolver の実装を作成してください。次の例では Host ヘッダーを使って適切な設定を見つけ、それとアプリケーションのJavaクラスパスから関連する要素をロードしています。

package example;

import java.io.InputStream;
import org.keycloak.adapters.saml.SamlConfigResolver;
import org.keycloak.adapters.saml.SamlDeployment;
import org.keycloak.adapters.saml.config.parsers.DeploymentBuilder;
import org.keycloak.adapters.saml.config.parsers.ResourceLoader;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.saml.common.exceptions.ParsingException;

public class SamlMultiTenantResolver implements SamlConfigResolver {

    @Override
    public SamlDeployment resolve(HttpFacade.Request request) {
        String host = request.getHeader("Host");
        String realm = null;
        if (host.contains("tenant1")) {
            realm = "tenant1";
        } else if (host.contains("tenant2")) {
            realm = "tenant2";
        } else {
            throw new IllegalStateException("Not able to guess the keycloak-saml.xml to load");
        }

        InputStream is = getClass().getResourceAsStream("/" + realm + "-keycloak-saml.xml");
        if (is == null) {
            throw new IllegalStateException("Not able to find the file /" + realm + "-keycloak-saml.xml");
        }

        ResourceLoader loader = new ResourceLoader() {
            @Override
            public InputStream getResourceAsStream(String path) {
                return getClass().getResourceAsStream(path);
            }
        };

        try {
            return new DeploymentBuilder().build(is, loader);
        } catch (ParsingException e) {
            throw new IllegalStateException("Cannot load SAML deployment", e);
        }
    }
}

また、以下のように、 web.xmlkeycloak.config.resolver context-paramでどの SamlConfigResolver 実装を使うかを設定する必要もあります。

<web-app>
    ...
    <context-param>
        <param-name>keycloak.config.resolver</param-name>
        <param-value>example.SamlMultiTenantResolver</param-value>
    </context-param>
</web-app>

3.1.13. 以前のバージョンからの移行

1.9.0への移行
SAML SPクライアント・アダプターの変更

Keycloak SAML SPクライアント・アダプターでは、特定のエンドポイント( /saml )がIdPに登録されている必要があります。SamlFilterもまた、他のバインディングに加えて /saml にもバインドされている必要があります。SAML POSTバインディングがリクエスト入力ストリームを処理することになるため必要でした。これは、依存するクライアントにとって本当に悪いことです。

3.2. mod_auth_mellon Apache HTTPDモジュール

mod_auth_mellon モジュールは、SAML用のApache HTTPDプラグインです。使用している言語/環境がApache HTTPDをプロキシーとして使用することをサポートしている場合、mod_auth_mellonを使用してWebアプリケーションをSAMLでセキュリティー保護できます。このモジュールの詳細については、 mod_auth_mellon のGitHubリポジトリーを参照してください。

mod_auth_mellonを設定するには、以下が必要です。

  • アイデンティティ・プロバイダー(IdP)のエンティティー記述子XMLファイル。Keycloakまたは別のSAML IdPへの接続情報を記述します。

  • SPエンティティー記述子XMLファイル。セキュアにするアプリケーションのSAML接続と設定を記述します。

  • 秘密鍵PEMファイル。アプリケーションが文書に署名するために使用する秘密鍵を定義するPEM形式のテキストファイルです。

  • 証明書のPEMファイル。アプリケーションの証明書を定義するテキストファイルです。

  • mod_auth_mellon特有のApache HTTPDモジュールの設定。

Keycloakアプリケーション・サーバーのレルム内にクライアント・アプリケーションを定義して登録している場合、KeycloakはApache HTTPDモジュールの設定を除くすべての必要なファイルを生成できます。

Apache HTTPDモジュールの設定を生成するには、以下のステップを実行します。

  1. SAMLクライアントのインストール・ページに移動し、Mod Auth Mellon filesオプションを選択します。

    mod_auth_mellon設定のダウンロード

    mod auth mellon config download

  2. 必要なXML記述子とPEMファイルを含むzipファイルをダウンロードするには、 Download をクリックします。

3.2.1. mod_auth_mellonのKeycloakとの設定

関連するホストは2つあります。

  • Keycloakが実行されているホスト。KeycloakがSAMLアイデンティティー・プロバイダー(IdP)であるため、$idp_hostと呼ばれます。

  • Webアプリケーションが実行されているホスト。$sp_hostと呼ばれます。SAMLでは、IdPを使用するアプリケーションをサービス・プロバイダー(SP)と呼びます。

次のすべての手順は、root特権で$sp_hostに対して実行される必要があります。

パッケージのインストール

必要なパッケージをインストールするには、次のものが必要です。

  • Apache Webサーバー(httpd)

  • Apache用のMellon SAML SPアドオン・モジュール

  • X509証明書を作成するためのツール

必要なパッケージをインストールするには、次のコマンドを実行します。

yum install httpd mod_auth_mellon mod_ssl openssl
Apache SAMLの設定ディレクトリーの作成

ApacheのSAML使用に関する設定ファイルは、1か所に保存することをお勧めします。

Apache設定ルート(/etc/httpd)の下に、saml2という名前の新しいディレクトリーを作成します。

mkdir /etc/httpd/saml2
Mellonサービス・プロバイダーの設定

Apacheアドオン・モジュールの設定ファイルは/etc/httpd/conf.dディレクトリーにあり、拡張子は.confです。/etc/httpd/conf.d/mellon.confファイルを作成し、Mellonの設定ディレクティブをその中に置く必要があります。

Mellonの設定ディレクティブは、大まかに2つのクラスの情報に分類できます。

  • SAML認証で保護するURL

  • 保護されたURLが参照されるときに使用されるSAMLパラメーター。

Apacheの設定ディレクティブは通常、URL空間の階層ツリー構造に従います。これらは、ロケーションとして知られています。Mellonが保護するURLのロケーションを1つ以上指定する必要があります。各場所に適用される設定パラメーターの追加方法には柔軟性があります。ロケーション・ブロックに必要なすべてのパラメーターを追加するか、特定の保護されたロケーションが継承するURLロケーション階層の上位の共通の場所(またはその両方の組み合わせ)にMellonパラメーターを追加できます。どのロケーションがSAMLアクションをトリガーするかにかかわらず、同じ方法でSPが動作するのが一般的であるため、ここで使用されている設定例では、Mellon設定ディレクティブを階層のルートに置き、Mellonによって保護される特定のロケーションを最小限のディレクティブで定義できます。この戦略は、保護された各ロケーションで同じパラメーターを複製することを回避します。

この例では、保護された場所は https://$sp_host/private 1つだけです。

Mellonサービス・プロバイダーを設定するには、次の手順を実行します。

  1. /etc/httpd/conf.d/mellon.confファイルを次の内容で作成します。

 <Location / >
    MellonEnable info
    MellonEndpointPath /mellon/
    MellonSPMetadataFile /etc/httpd/saml2/mellon_metadata.xml
    MellonSPPrivateKeyFile /etc/httpd/saml2/mellon.key
    MellonSPCertFile /etc/httpd/saml2/mellon.crt
    MellonIdPMetadataFile /etc/httpd/saml2/idp_metadata.xml
 </Location>
 <Location /private >
    AuthType Mellon
    MellonEnable auth
    Require valid-user
 </Location>
上記のコードで参照されているファイルの一部は、後の手順で作成されます。

3.2.2. mod_auth_mellonが使用するCookieのSameSite値を設定する

ブラウザーは、Cookieの SameSite 属性のデフォルト値を Lax に設定することを計画しています。この設定は、リクエストが同じドメインで発生した場合にのみCookieがアプリケーションに送信されることを意味します。この動作はSAML POSTバインディングに影響を与え、機能しなくなる可能性があります。 mod_auth_mellon モジュールの完全な機能を保持するために、 mod_auth_mellon モジュールによって作成されたCookieの SameSite 値を None に設定することをお勧めします。そうしないと、Keycloakを使用してログインできなくなる場合があります。

SameSite の値を None に設定するには、次の設定を mellon.conf ファイル内の <Location / > タグに追加します。

MellonSecureCookie On
MellonCookieSameSite none

この設定のサポートは、バージョン0.16.0以降の mod_auth_mellon モジュールで利用できます。

サービス・プロバイダーのメタデータの作成

SAMLではIdPとSPがXML形式のSAMLメタデータを交換します。メタデータのスキーマは標準であるため、参加するSAMLエンティティーが互いのメタデータを消費できることが保証されます。必要なものは次のとおりです。

  • SPが利用するIdPのメタデータ

  • IdPに提供されたSPを記述するメタデータ

SAMLメタデータのコンポーネントの1つはX509証明書です。この証明書は、2つの目的で使用されます。

  • 受信側が予想される相手から発信されたメッセージを証明できるように、SAMLメッセージに署名します。

  • トランスポート中にメッセージを暗号化します(ほとんどの場合、SAMLメッセージはTLSで保護されたトランスポートで発生するため、使用されません)

すでに認証局(CA)がある場合、または自己署名証明書を生成できる場合は、独自の証明書を使用できます。この例では、簡単のため、自己署名証明書が使用されています。

MellonのSPメタデータは、mod_auth_mellonのインストールされたバージョンの機能を反映する必要があるため、有効なSPメタデータXMLでなければならず、X509証明書(X509証明書の生成に慣れていない限り、その作成は難解になることがあります)を含む必要があります。SPメタデータを生成する最も便利な方法は、mod_auth_mellonパッケージに含まれているツール( mellon_create_metadata.sh )を使用することです。生成されたメタデータは、テキストファイルであるため、後で編集することができます。このツールは、X509の鍵と証明書も作成します。

SAMLのIdPとSPは、EntityIDという固有の名前を使用して自分自身を識別します。Mellonのメタデータ作成ツールを使用するには、次のものが必要です。

  • EntityID。通常SPのURLであり、しばしばSPメタデータを取得できるSPのURLです。

  • SPのSAMLメッセージが消費されるURL(MellonがMellonEndPointPathを呼び出すURL)。

SPメタデータを作成するには、次の手順を実行します。

  1. ヘルパーのシェル変数をいくつか作成します。

    fqdn=`hostname`
    mellon_endpoint_url="https://${fqdn}/mellon"
    mellon_entity_id="${mellon_endpoint_url}/metadata"
    file_prefix="$(echo "$mellon_entity_id" | sed 's/[^A-Za-z.]/_/g' | sed 's/__*/_/g')"
  2. 次のコマンドを実行して、Mellonメタデータ作成ツールを呼び出します。

    /usr/libexec/mod_auth_mellon/mellon_create_metadata.sh $mellon_entity_id $mellon_endpoint_url
  3. 生成されたファイルを目的の場所に移動します(上記の/etc/httpd/conf.d/mellon.confファイルを参照)。

    mv ${file_prefix}.cert /etc/httpd/saml2/mellon.crt
    mv ${file_prefix}.key /etc/httpd/saml2/mellon.key
    mv ${file_prefix}.xml /etc/httpd/saml2/mellon_metadata.xml
Keycloakアイデンティティー・プロバイダーにMellonサービス・プロバイダーを追加する

仮定: KeycloakのIdPはすでに$idp_hostにインストールされています。

Keycloakは複数のテナントをサポートしており、すべてのユーザー、クライアントなどがレルムと呼ばれる領域にグループ化されます。各レルムは他のレルムとは独立しています。Keycloakに既存のレルムを使用できますが、この例では、test_realmという新しいレルムを作成し、そのレルムを使用する方法を示しています。

これらの操作はすべて、Keycloak管理Webコンソールを使用して実行されます。$idp_hostの管理者ユーザー名とパスワードが必要です。

次の手順を完了してください。

  1. 管理コンソールを開き、管理者のユーザー名とパスワードを入力してログオンします。

    管理コンソールにログインすると、既存のレルムが存在します。Keycloakを最初に設定すると、デフォルトでルート・レルムのmasterが作成されます。以前に作成されたレルムは、管理コンソールの左上隅にドロップダウン・リストに表示されます。

  2. レルムのドロップダウン・リストから Add realm を選択します。

  3. 名前フィールドに test_realm と入力し、 Create をクリックします。

Mellonサービス・プロバイダーをレルムのクライアントとして追加する

Keycloakでは、SAML SPはクライアントと呼ばれます。SPを追加するには、レルムのクライアントのセクションに移動します。

  1. 左側のClientsメニュー項目をクリックし、右上の Create をクリックして新しいクライアントを作成します。

Mellon SPクライアントの追加

Mellon SPクライアントを追加するには、次の手順を実行します。

  1. クライアント・プロトコルをSAMLに設定します。Client Protocolドロップダウン・リストから、 saml を選択します。

  2. 上記で作成したMellon SPメタデータ・ファイル(/etc/httpd/saml2/mellon_metadata.xml)を提供します。ブラウザーが実行されている場所によっては、$sp_hostからブラウザーが実行されているマシンにSPメタデータをコピーして、ブラウザーがそのファイルを見つけなければならない場合があります。

  3. Save をクリックします。

Mellon SPクライアントの編集

いくつかのクライアント設定パラメータがあります。

  • "Force POST Binding"がオンになっていることを確認します。

  • 有効なリダイレクトURIリストにpaosResponseを追加します。

    1. "Valid Redirect URIs"内のpostResponse URLをコピーし、 "+"のすぐ下の空の追加テキスト・フィールドに貼り付けます。

    2. "postResponse"を"paosResponse"に変更します(SAML ECPにはpaosResponse URLが必要です)。

    3. 下部の Save をクリックしてください。

多くのSAML SPは、グループ内のユーザーのメンバーシップに基づいて認可を決定します。KeycloakのIdPはユーザー・グループ情報を管理できますが、IdPがSAML属性としてそれを提供するように設定されていない限り、ユーザーのグループは提供されません。

ユーザーのグループをSAML属性として提供するようにIdPを設定するには、次の手順を実行します。

  1. クライアントのMapperタブをクリックします。

  2. Mapperページの右上にある Create をクリックします。

  3. Mapper Typeのドロップダウン・リストから Group list を選択します。

  4. "group list"に名前を設定します。

  5. "groups"にSAML属性名を設定します。

  6. Save をクリックします。

残りの手順は$sp_hostで実行されます。

アイデンティティー・プロバイダー・メタデータの取得

IdPでレルムを作成したので、それに関連付けられたIdPメタデータを取得して、Mellon SPがそれを認識できるようにする必要があります。前に作成した/etc/httpd/conf.d/mellon.confファイルでは、MellonIdPMetadataFileは/etc/httpd/saml2/idp_metadata.xmlとして指定されていますが、そのファイルは$sp_host上に存在しませんでした。そのファイルを取得するには、IdPからファイルを検索します。

  1. $idp_hostを正しい値に置き換えて、IdPからファイルを取得します。

    curl -k -o /etc/httpd/saml2/idp_metadata.xml \
    https://$idp_host/auth/realms/test_realm/protocol/saml/descriptor

    これでMellonの設定は完了となります。

  2. Apache設定ファイルの構文チェックを実行するには、次のようにします。

    apachectl configtest
    Configtestは apachectl の -t 引数に相当します。設定テストでエラーが示された場合は、先に進む前に修正してください。
  3. Apacheサーバーを再起動します。

    systemctl restart httpd.service

$ idp_host のIdPに対して認証することで、$sp_host/protected(とその配下すべて)のURLを保護するように、test_realmのSAML IdPとしてKeycloakを、SAML SPとしてmod_auth_mellonを設定しました。

4. Dockerレジストリーの設定

Docker認証はデフォルトで無効です。有効にするには、Profilesを参照してください。

このセクションでは、Keycloakを認証サーバーとして使用するようにDockerレジストリーを設定する方法について説明します。

Dockerレジストリーを設定および設定する方法の詳細については、Docker Registry Configuration Guideを参照してください。

4.1. Dockerレジストリーの設定ファイルのインストール

より高度なDockerレジストリー設定を持つユーザーの場合は、通常、独自のレジストリー設定ファイルを提供することをお勧めします。Keycloak Dockerプロバイダーは、 Registry Config File Format Optionを使用してこのメカニズムをサポートしています。このオプションを選択すると、次のような出力が生成されます。

auth:
  token:
    realm: http://localhost:8080/auth/realms/master/protocol/docker-v2/auth
    service: docker-test
    issuer: http://localhost:8080/auth/realms/master

この出力は、既存のレジストリー設定ファイルにコピーできます。ファイルの設定方法の詳細については、レジストリー設定ファイルの仕様を参照してください。または、基本的な例から始めてください。

rootcertbundle フィールドにKeycloakのレルムの公開証明書の場所を設定するのを忘れないでください。auth設定は、この引数なしでは動作しません。

4.2. Dockerレジストリー環境変数のオーバーライド・インストール

開発やPOC用のDockerレジストリーに単純な環境変数のオーバーライドを使用することが適切な場合がよくあります。このアプローチは、通常プロダクション環境での使用では推奨されていませんが、レジストリーを立ち上げるための安上がりな方法が必要な場合に役立ちます。クライアント・インストール・タブから Variable Override Format Optionを使用するだけで、出力は次のようになります。

REGISTRY_AUTH_TOKEN_REALM: http://localhost:8080/auth/realms/master/protocol/docker-v2/auth
REGISTRY_AUTH_TOKEN_SERVICE: docker-test
REGISTRY_AUTH_TOKEN_ISSUER: http://localhost:8080/auth/realms/master
REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE オーバーライドをKeycloakレルムの公開証明書の場所で設定するのを忘れないでください。auth設定は、この引数なしでは動作しません。

4.3. Docker Compose YAMLファイル

このインストール方法は、Keycloakサーバーで認証するDockerレジストリーを構築する簡易的な方法です。これは開発目的のみを対象としており、プロダクション環境やそれと同等の環境には使用しないでください。

zipファイルのインストール・メカニズムは、KeycloakサーバーがDockerレジストリーとどのようにやりとりができるかを理解したい開発者のために、クイックスタートを提供します。 下記のとおりに設定します。

  1. 目的のレルムから、クライアント設定を作成します。この時点で、Dockerレジストリーはありません。クイックスタートがその部分を担当します。

  2. インストール・タブから"Docker Compose YAML"オプションを選択し、.zipファイルをダウンロードします。

  3. アーカイブを目的の場所に解凍し、ディレクトリーを開きます。

  4. Dockerレジストリーを docker-compose up で起動します。

HTTP BASIC認証のフローはフォームを提示しないので、Dockerレジストリー・クライアントを’master’以外のレルムに設定することをお勧めします。

上記の設定が完了し、KeycloakサーバーとDockerレジストリーが実行されたら、Docker認証が成功するはずです。

[user ~]# docker login localhost:5000 -u $username
Password: *******
Login Succeeded

5. クライアントの登録

Keycloakをアプリケーションまたはサービスで使用するには、Keycloakにクライアントを登録する必要があります。管理者は管理コンソール(または管理RESTエンドポイント)にて登録できますが、クライアント自身でもKeycloakクライアント登録サービスにおいて登録することはできます。

クライアント登録サービスには、Keycloak Client Representations、OpenID Connect Client Meta DataおよびSAML Entity Descriptorsのビルトインサポートが用意されています。クライアント登録サービスのエンドポイントは /auth/realms/<realm>/clients-registrations/<provider> です。

サポートされている組み込みの provider は以下のとおりです。

  • default - Keycloak Client Representation(JSON)

  • install - Keycloakアダプター設定 (JSON)

  • openid-connect - OpenID Connectクライアント・メタデータ・ディスクリプション(JSON)

  • saml2-entity-descriptor - SAMLエンティティー記述子(XML)

以下のセクションでは、異なるプロバイダーを使用する方法を説明します。

5.1. 認証

クライアント登録サービスを呼び出すには、通常トークンが必要です。トークンは、ベアラートークン、初期アクセストークン、登録アクセストークンのいずれかです。いずれのトークンも含まず、同様に新規クライアントを登録する方法がありますが、クライアント登録ポリシー(下記参照)を設定する必要があります。

5.1.1. ベアラートークン

ベアラートークンは、ユーザーやサービス・アカウントに代わって発行されます。エンドポイントを呼び出すには、次のパーミッションが必要です(詳細についてはServer Administration Guideを参照してください)。

  • create-client または manage-client - クライアントの作成権限

  • view-client または manage-client - クライアントの参照権限

  • manage-client - クライアントの更新または削除権限

クライアントを作成するためにベアラートークンを使用する場合、 create-client ロールのみを持つサービス・アカウントのトークンを使用することをお勧めします(詳細については、Server Administration Guideを参照してください)。

5.1.2. 初期アクセストークン

新しいクライアントを登録するための推奨される方法は、初期アクセストークンを使用することです。初期アクセストークンは、クライアントを作成する場合にのみ使用でき、有効期限が設定できるだけでなく、クライアントを作成可能な回数の上限も設定できます。

初期アクセストークンは、管理コンソールで作成できます。新たに初期アクセストークンを作成するには、まず管理コンソールでレルムを選択し、左側のメニューの Realm Settings 、続いてページに表示されるタブの Client Registration をクリックします。そして、最後に Initial Access Tokens のサブタブをクリックします。

これにより既存の初期アクセストークンを参照できるようになります。アクセスできる場合は、不要になったトークンを削除できます。トークン作成時のみその値を取得できます。 Create をクリックして新しいトークンを作成します。必要に応じてトークンの有効期間や、トークンを使用して作成することができるクライアント数を追加できます。 Save をクリックすると、トークンの値が表示されます。

後でトークンを取得することはできないので、このトークンをコピー/ペーストすることが重要です。コピー/ペーストすることを忘れた場合は、そのトークンを削除し、別のトークンを作成してください。

リクエストにAuthorizationヘッダーを追加することにより、トークンの値はクライアント登録サービスを呼び出す際の標準ベアラートークンとして使用されます。以下に例を示します。

Authorization: bearer eyJhbGciOiJSUz...

5.1.3. 登録アクセストークン

クライアント登録サービスを介してクライアントを作成する際には、レスポンスには登録アクセストークンが含まれます。登録アクセストークンは、クライアント設定を取得するだけでなく、クライアントの更新や削除のためのアクセス権を提供します。登録アクセストークンは、ベアラートークンや初期アクセストークンと同じ方法でリクエストに含まれます。登録アクセストークンは一度だけ有効であり、それを使用すると新しいトークンがレスポンスに含まれます。

クライアント登録サービス以外でクライアントが作成された場合、それに関連付けられた登録アクセストークンはありません。管理コンソールを介して、登録アクセストークンを作成できます。これは、特定のクライアントのトークンを紛失した場合に役立ちます。新しいトークンを作成するには、管理コンソールでクライアントを検索し、 Credentials をクリックします。そして、 Generate registration access token をクリックします。

5.2. Keycloak Representations

default クライアント登録プロバイダーは、クライアントを作成、取得、更新、削除するために使用できます。管理コンソールでの設定と同等なクライアント設定のサポートを提供する、Keycloak Client Representation形式を使用します。また、プロトコル・マッパーの設定例が含まれています。

クライアントを作成するには、Client Representation(JSON)を作成し、次にHTTP POSTリクエストを /auth/realms/<realm>/clients-registrations/default に送信します。

登録アクセストークンを含むClient Representationが返されます。設定を取得したり、クライアントを後で更新、削除したい場合は、登録アクセストークンをどこかに保存する必要があります。

Client Representationを取得するには、HTTP GETリクエストを /auth/realms/<realm>/clients-registrations/default/<client id> に送信します。

新しい登録アクセストークンも返します。

Client Representationを更新するには、更新したClient Representationを次のURLにHTTP PUTします: /auth/realms/<realm>/clients-registrations/default/<client id>

新しい登録アクセストークンも返します。

Client Representationを削除するには、次のURLにHTTP DELETEします: /auth/realms/<realm>/clients-registrations/default/<client id>

5.3. Keycloakアダプター設定

installation クライアント登録プロバイダーは、クライアントのアダプター設定を取得するために使用できます。トークン認証に加えて、HTTP BASIC認証を使用してクライアント・クレデンシャルで認証することもできます。 これを行うには、リクエストに次のヘッダーを含めます。

Authorization: basic BASE64(client-id + ':' + client-secret)

アダプター設定を取得するには、HTTP GETリクエストを /auth/realms/<realm>/clients-registrations/install/<client id> に送信します。

パブリック・クライアントは認証が要求されません。これは、JavaScriptアダプターは、上記URLを使用して、直接Keycloakからクライアント設定を読み込めることを意味します。

5.4. OpenID Connect動的クライアント登録

これらの仕様を使用した、Keycloakにクライアントを登録するエンドポイントは、 /auth/realms/<realm>/clients-registrations/openid-connect[/<client id>] です。

このエンドポイントは、そのレルムのOpenID Connect Discoveryエンドポイント( /auth/realms/<realm>/.well-known/openid-configuration )でも見つかります。

5.5. SAMLエンティティー記述子

SAMLエンティティー記述子エンドポイントは、クライアントを作成するために、SAML v2エンティティー記述子を使用することだけをサポートします。クライアントの取得、更新、削除はサポートしていません。これらの操作に対しては、Keycloak represendationのエンドポイントを使用してください。クライアントを作成すると、作成されたクライアントの詳細とともに、Keycloak Client Representationが返されます(登録アクセストークンを含みます)。

クライアントを作成するには、SAMLエンティティー記述子を使用して /auth/realms/<realm>/clients-registrations/saml2-entity-descriptor にHTTP POSTリクエストを送信します。

5.6. CURLを使用した例

CURLを使用して、クライアントID myclient でクライアントを作成する例を次に示します。適切な初期アクセストークンまたはベアラートークンで eyJhbGciOiJSUz…​ を置き換える必要があります。

curl -X POST \
    -d '{ "clientId": "myclient" }' \
    -H "Content-Type:application/json" \
    -H "Authorization: bearer eyJhbGciOiJSUz..." \
    http://localhost:8080/auth/realms/master/clients-registrations/default

5.7. Javaクライアント登録APIを使用した例

クライアント登録Java APIは、Javaを使用してクライアント登録サービスを使いやすくします。使用するには、Mavenに org.keycloak:keycloak-client-registration-api:>VERSION< の依存関係を含めてください。

クライアント登録の使用方法の完全な説明は、JavaDocsを参照してください。以下は、クライアントを作成する例です。 eyJhbGciOiJSUz…​ を適切な初期アクセストークンまたはベアラートークンに置き換える必要があります。

String token = "eyJhbGciOiJSUz...";

ClientRepresentation client = new ClientRepresentation();
client.setClientId(CLIENT_ID);

ClientRegistration reg = ClientRegistration.create()
    .url("http://localhost:8080/auth", "myrealm")
    .build();

reg.auth(Auth.token(token));

client = reg.create(client);

String registrationAccessToken = client.getRegistrationAccessToken();

5.8. クライアント登録ポリシー

Keycloakは現在、クライアント登録サービスを介して新しいクライアントを登録できる2つの方法をサポートしています。

  • 認証済みリクエスト - 新しいクライアントを登録するためのリクエストは、前述の 初期アクセストークン または ベアラートークン のいずれかを含める必要があります。

  • 匿名リクエスト - 新しいクライアントを登録するためのリクエストは、すべての任意のトークンを含める必要はありません。

匿名クライアント登録リクエストは、非常に面白く、強力な機能ですが、誰でも制限なく新しいクライアントを登録できることを通常は望みません。したがって、そのような条件下で、新しいクライアントを登録できるユーザーを制限する方法を提供する、 Client Registration Policy SPI があります。

Keycloak管理コンソールで、 Client Registration タブ、 Client Registration Policies サブタブをクリックします。ここでは、匿名リクエストに対してデフォルトでどのようなポリシーが設定されるか、認証リクエストに対してどのようなポリシーを設定されるかが表示されます。

匿名リクエスト(トークンの無いリクエスト)は、新しいクライアントを作成(登録)することだけが許可されています。したがって、匿名リクエストを使用して新しいクライアントを登録すると、レスポンスにはそのクライアントの読み取り、更新、削除リクエストに使用する登録アクセストークンが含まれます。ただし、匿名登録レスポンスから取得した登録アクセストークンを使用すると、匿名ポリシーの対象にもなります!これは、たとえば、 Trusted Hosts ポリシーがあるならば、クライアント更新リクエストも信頼されたホストから送信される必要があることを意味します。また、たとえば、クライアントの更新時に Consent Required ポリシーが存在する場合に、 Consent Required を無効にするようなことはできません。

現在、以下のポリシーの実装があります。

  • Trusted Hosts Policy - 信頼されたホストと信頼されているドメインの一覧を設定できます。それらのホストやドメインだけからクライアント登録サービスへのリクエストを送信できます。信頼されていないIPから送信されたリクエストは拒否されます。また、新たに登録されたクライアントのURLは、信頼されたホストやドメインだけを使わなければなりません。たとえば、信頼できないホストを指すクライアントの Redirect URI を設定することは許可されません。デフォルトでは、ホワイトリストに登録されているホストが無いため、匿名クライアント登録は事実上無効になっています。

  • Consent Required Policy - 新たに登録されたクライアントは、 Consent Allowed スイッチが有効になります。したがって、認証成功後、パーミッション(クライアント・スコープ)を承認する必要がある場合、常に同意画面が表示されます。つまり、ユーザーが承認しない限り、クライアントはユーザーの個人情報やパーミッションにアクセスすることができません。

  • Protocol Mappers Policy - プロトコル・マッパー実装のホワイトリストを設定できます。ホワイトリストに無いプロトコル・マッパーが含まれている場合は、新たなクライアントを登録または更新できません。このポリシーは認証済みのリクエストにも使用されるため、認証済みのリクエストに対しても、どのプロトコル・マッパーを使用できるかの制限があることに注意してください。

  • Client Scope Policy - 新たに登録または更新したクライアントで使うことができる Client Scopes のホワイトリスト化を許可します。デフォルトでは、ホワイトリスト化されたスコープはありません。 Realm Default Client Scopes として定義されているクライアント・スコープのみがデフォルトでホワイトリストに登録されています。

  • Full Scope Policy - 新たに登録されたクライアントは Full Scope Allowed スイッチが無効に切り替わります。これは、スコープが設定されたレルム・ロールや他のクライアントのクライアント・ロールが無いことを意味します。

  • Max Clients Policy - レルム内の現在のクライアント数が指定された制限以上の場合、登録を拒否します。匿名登録のデフォルトは200です。

  • Client Disabled Policy - 新たに登録されたクライアントは無効になります。これは、管理者が新しく登録された全てのクライアントを手動で承認し、有効にする必要があることを意味します。このポリシーは、匿名登録でもデフォルトでは使用されません。

6. クライアント登録CLI

クライアント登録CLIは、アプリケーション開発者がKeycloakと統合する際にセルフ・サービスで新しいクライアントを設定するためのコマンドライン・インターフェイス(CLI)ツールです。 これは、Keycloakクライアント登録RESTエンドポイントと対話するように特別に設計されています。

Keycloakを使用できるように、任意のアプリケーションのクライアント設定を作成または取得する必要があります。通常は、一意のホスト名でホストされる新しいアプリケーションごとに、新しいクライアントを設定します。アプリケーションがKeycloakと対話する際に、アプリケーションはクライアントIDで自身を識別し、Keycloakはログイン・ページ、シングル・サインオン(SSO)セッション管理、およびその他のサービスを提供できます。

アプリケーション・クライアントは、クライアント登録CLIを使用してコマンド・ラインから設定でき、シェルスクリプトで使用できます。

特定のユーザーが クライアント登録CLI を使用できるようにするため、通常、Keycloak管理者は管理コンソールを使って新しいユーザーを適切なロールで設定するか、クライアント登録REST APIの権限を与えるように新しいクライアントとクライアント・シークレットを設定します 。

6.1. クライアント登録CLIで使用するための新しい正規ユーザーの設定

  1. 管理コンソール(たとえば、 http://localhost:8080/auth/admin)に admin としてログインします。

  2. 管理するレルムを選択します。

  3. 既存のユーザーを使用する場合は、編集するユーザーを選択します。それ以外の場合は、新しいユーザーを作成します。

  4. Role Mappings > Client Roles > realm-management を選択します。masterレルムにいる場合は、NAME-realm を選択します。ここで、 NAME はターゲット・レルムの名前です。masterレルム内のユーザーに他のレルムへのアクセス権を与えることができます。

  5. Available Roles > manage-client を選択して、クライアント管理権限のフルセットを付与します。別の選択肢として、読み取り専用に view-clients を、または新しいクライアント作成用に create-client を選択します。

    これらの権限は、ユーザーに初期アクセストークンまたは登録アクセストークンを使用せずに操作を実行できるようにします。

realm-management ロールをユーザーに割り当てることはできません。その場合、ユーザーはクライアント登録CLIでログインできますが、初期アクセストークンなしでは使用できません。トークンなしで操作を実行しようとすると、 403 Forbidden エラーが発生します。

管理者は、管理コンソールの Realm Settings > Client Registration > Initial Access Token メニューから初期アクセストークンを発行できます。

6.2. クライアント登録CLIで使用するためのクライアントの設定

デフォルトでは、サーバーはクライアント登録CLIを admin-cli クライアントとして認識します。これは新規レルムごとに自動的に設定されます。ユーザー名でログインする場合、追加のクライアント設定は必要ありません。

  1. クライアント登録CLIに別のクライアント設定を使用したい場合は、新しいクライアントを作成します(たとえば、 reg-cli )。

  2. Standard Flow EnabledOff に設定します。

  3. クライアント Access TypeConfidential に設定し、 Credentials > ClientId and Secret を選択することで、セキュリティーを強化してください。

    Credentials タブの下で、 Client Id and Secret または Signed JWT のいずれかを設定できます。

  4. クライアントに関連付けられたサービス・アカウントを使用したい場合は、 管理コンソールClients セクションで編集するクライアントを選択することで、サービス・アカウントを有効にします。

    1. Settings で、 Access TypeConfidential に変更し、 Service Accounts Enabled の設定を On に切り替え、 Save をクリックします。

    2. Service Account Roles をクリックし、目的のロールを選択してサービス・アカウントのアクセスを設定します。選択するロールの詳細については、クライアント登録CLIで使用するための新しい正規ユーザーの設定を参照してください。

  5. サービス・アカウントの代わりに通常のユーザー・アカウントを使用する場合は、 Direct Access Grants Enabled の設定を On に切り替えます。

  6. クライアントが Confidential として設定されている場合、--secret オプションを使用して、 kcreg config credentials を実行する際に設定されたシークレットを提供してください。

  7. kcreg config credentials を実行する際に使用する clientId を指定します(たとえば、 --client reg-cli )。

  8. サービス・アカウントを有効にすると、 kcreg config credentials を実行するときにユーザーを指定せずに、クライアント・シークレットまたはキーストア情報のみを提供することができます。

6.3. クライアント登録CLIのインストール

クライアント登録CLIは、Keycloakサーバーの配布物内にパッケージ化されています。実行スクリプトは bin ディレクトリにあります。Linuxスクリプトは kcreg.sh で、Windowsスクリプトは kcreg.bat です。

ファイル・システム上の任意の場所からクライアントを使用できるように設定するときは、Keycloakサーバーのディレクトリーを PATH に追加してください。

以下に例を示します。

  • Linuxの場合:

$ export PATH=$PATH:$KEYCLOAK_HOME/bin
$ kcreg.sh
  • Windowsの場合:

c:\> set PATH=%PATH%;%KEYCLOAK_HOME%\bin
c:\> kcreg

KEYCLOAK_HOME は、Keycloakサーバーの配布物が解凍されたディレクトリーを示します。

6.4. クライアント登録CLIの使用

  1. クレデンシャルでログインして、認証されたセッションを開始します。

  2. Client Registration REST エンドポイントでコマンドを実行します。

    以下に例を示します。

    • Linuxの場合:

      $ kcreg.sh config credentials --server http://localhost:8080/auth --realm demo --user user --client reg-cli
      $ kcreg.sh create -s clientId=my_client -s 'redirectUris=["http://localhost:8980/myapp/*"]'
      $ kcreg.sh get my_client
    • Windowsの場合:

      c:\> kcreg config credentials --server http://localhost:8080/auth --realm demo --user user --client reg-cli
      c:\> kcreg create -s clientId=my_client -s "redirectUris=[\"http://localhost:8980/myapp/*\"]"
      c:\> kcreg get my_client

      プロダクション環境では、ネットワーク・スニファーへのトークンの公開を避けるため、 https: でKeycloakにアクセスする必要があります。

  3. サーバーの証明書が、Javaのデフォルトの証明書トラストストアに含まれている信頼できる証明機関(CA)のいずれかによって発行されていない場合は、 truststore.jks ファイルを準備し、クライアント登録CLIに使用するよう指示します。

    以下に例を示します。

    • Linuxの場合:

      $ kcreg.sh config truststore --trustpass $PASSWORD ~/.keycloak/truststore.jks
    • Windowsの場合:

      c:\> kcreg config truststore --trustpass %PASSWORD% %HOMEPATH%\.keycloak\truststore.jks

6.4.1. ログイン

  1. クライアント登録CLIを使用してログインする際は、サーバーのエンドポイントURLとレルムを指定します。

  2. ユーザー名またはクライアントIDを指定すると、特別なサービス・アカウントが使用されます。ユーザー名を使用する場合は、指定されたユーザーのパスワードを使用する必要があります。クライアントIDを使用する場合は、パスワードの代わりにクライアント・シークレットまたは Signed JWT を使用します。

ログイン方法にかかわらず、ログインするアカウントには、クライアント登録操作を実行するための適切な権限が必要です。master以外のレルムのアカウントは、同じレルム内のクライアントを管理する権限しか持てません。異なるレルムを管理する必要がある場合は、複数のユーザーを異なるレルムで設定するか、 master レルムで1人のユーザーを作成して、異なるレルムのクライアントを管理するロールを追加します。

クライアント登録CLIでユーザーを設定することはできません。管理コンソールのWebインターフェイスまたは管理クライアントCLIを使用して、ユーザーを設定します。詳細については、Server Administration Guideを参照してください。

kcreg が正常にログインすると、認可トークンを受信してプライベート設定ファイルに保存し、その後の呼び出しでトークンを使用できるようにします。設定ファイルの詳細については、代替設定の使用を参照してください。

クライアント登録CLIの使用方法の詳細については、組み込みのヘルプを参照してください。

以下に例を示します。

  • Linuxの場合:

$ kcreg.sh help
  • Windowsの場合:

c:\> kcreg help

認証されたセッションの開始についての詳細は、 kcreg config credentials --help を参照してください。

6.4.2. 代替設定の使用

デフォルトでは、クライアント登録CLIは、ユーザーのホーム・ディレクトリー配下のデフォルトの場所に設定ファイル( ./.keycloak/kcreg.config )を自動的に保存します。 --config オプションを使うと別のファイルや場所を指すことができ、複数の認証済みセッションを並行して処理できます。1つのスレッドから1つの設定ファイルに結び付けられた操作を実行するのが最も安全な方法です。

システム上の他のユーザーが設定ファイルを参照できないようにしてください。設定ファイルには、非公開にしておくべきアクセストークンとシークレットが含まれています。

利便性が低く、より多くのトークン要求が必要ではありますが、すべてのコマンドで --no-config オプションを使用することで、設定ファイル内にシークレットを保存しないようにすることができます。 kcreg 呼び出しごとにすべての認証情報を指定してください。

6.4.3. 初期アクセストークンと登録アクセストークン

使用したいKeycloakサーバーで設定されたアカウントを持っていない開発者は、クライアント登録CLIを使用できます。レルム管理者が開発者に初期アクセストークンを発行したときだけ、開発者は使用することができます。これらのトークンをいつどのように発行し配布するかは、レルム管理者が決定します。レルム管理者は、初期アクセストークンの最大経過時間と、初期アクセストークンを使用して作成できるクライアントの総数を制限できます。

開発者が初期アクセストークンを取得すると、開発者はこれを使用して、 kcreg config credentials で認証せずに新しいクライアントを作成できます。初期アクセストークンは、設定ファイルに保存するか、 kcreg create コマンドの一部として指定することができます。

以下に例を示します。

  • Linuxの場合:

$ kcreg.sh config initial-token $TOKEN
$ kcreg.sh create -s clientId=myclient

または

$ kcreg.sh create -s clientId=myclient -t $TOKEN
  • Windowsの場合:

c:\> kcreg config initial-token %TOKEN%
c:\> kcreg create -s clientId=myclient

または

c:\> kcreg create -s clientId=myclient -t %TOKEN%

初期アクセストークンを使用する場合、サーバー・レスポンスには新しく発行された登録アクセストークンが含まれます。そのクライアントの後続の操作は、そのクライアントに対してのみ有効なトークンで認証することによって実行される必要があります。

クライアント登録CLIはプライベートな設定ファイルを自動的に使用して、このトークンを関連付けられたクライアントと共に保存して使用します。すべてのクライアント操作で同じ設定ファイルが使用されている限り、開発者はこのように作成されたクライアントの読み取り、更新、または削除をするために、認証する必要はありません。

初期アクセストークンおよび登録アクセストークンの詳細については、クライアント登録を参照してください。

クライアント登録CLIでトークンを設定する方法の詳細については、 kcreg config initial-token --helpkcreg config registration-token --help コマンドを実行してください。

6.4.4. クライアント設定の作成

クレデンシャルで認証したり、初期アクセストークンを設定した後の最初のタスクは、通常、新しいクライアントを作成することです。準備されたJSONファイルをテンプレートとして使用し、いくつかの属性を設定または上書きすることがよくあります。

次の例は、JSONファイルを読み込み、それに含まれるクライアントIDを上書きし、他の属性を設定し、作成が成功した後に標準出力に設定を出力する方法を示しています。

  • Linuxの場合:

$ kcreg.sh create -f client-template.json -s clientId=myclient -s baseUrl=/myclient -s 'redirectUris=["/myclient/*"]' -o
  • Windowsの場合:

C:\> kcreg create -f client-template.json -s clientId=myclient -s baseUrl=/myclient -s "redirectUris=[\"/myclient/*\"]" -o

kcreg create コマンドの詳細については、 kcreg create --help を実行してください。

kcreg attrs を使って、利用可能な属性の一覧を取得できます。多くの設定属性の妥当性や一貫性はチェックされません。適切な値を指定する必要があります。また、テンプレートには id フィールドを持たせず、 kcreg create の引数として指定しないことを忘れないでください。

6.4.5. クライアント設定の取得

すでに存在するクライアントについては、 kcreg get コマンドを使用することで取得できます。

以下に例を示します。

  • Linuxの場合:

$ kcreg.sh get myclient
  • Windowsの場合:

C:\> kcreg get myclient

また、Webアプリケーションとパッケージ化できるアダプター設定ファイルとして、クライアント設定を取得することもできます。

以下に例を示します。

  • Linuxの場合:

$ kcreg.sh get myclient -e install > keycloak.json
  • Windowsの場合:

C:\> kcreg get myclient -e install > keycloak.json

kcreg get コマンドの詳細については、 kcreg get --help コマンドを実行してください。

6.4.6. クライアント設定の更新

クライアント設定の更新には2つの方法があります。

1つ目の方法は、現在の設定を取得してファイルに保存し、編集してから、新しい状態をサーバーにPOSTで送信する方法です。

以下に例を示します。

  • Linuxの場合:

$ kcreg.sh get myclient > myclient.json
$ vi myclient.json
$ kcreg.sh update myclient -f myclient.json
  • Windowsの場合:

C:\> kcreg get myclient > myclient.json
C:\> notepad myclient.json
C:\> kcreg update myclient -f myclient.json

2つ目の方法は、現在のクライアントをフェッチし、そのクライアント上のフィールドを設定または削除し、一度にPOSTで送信する方法です。

以下に例を示します。

  • Linuxの場合:

$ kcreg.sh update myclient -s enabled=false -d redirectUris
  • Windowsの場合:

C:\> kcreg update myclient -s enabled=false -d redirectUris

適用される変更のみを含むファイルを使用することもできるので、引数に多くの値を指定する必要はありません。この場合、 --merge をクライアント登録CLIに指定すると、JSONファイルは完全な新しい設定として扱われるのではなく、既存の設定に適用される属性のセットとして扱われます。

以下に例を示します。

  • Linuxの場合:

$ kcreg.sh update myclient --merge -d redirectUris -f mychanges.json
  • Windowsの場合:

C:\> kcreg update myclient --merge -d redirectUris -f mychanges.json

kcreg update コマンドの詳細については、 kcreg update --help コマンドを実行してください。

6.4.7. クライアント設定の削除

クライアントを削除するには、以下の例を使用します。

  • Linuxの場合:

$ kcreg.sh delete myclient
  • Windowsの場合:

C:\> kcreg delete myclient

kcreg delete コマンドの詳細については、 kcreg delete --help コマンドを実行してください。

6.4.8. 無効な登録アクセストークンのリフレッシュ

--no-config モードを使用して作成、読み込み、更新、削除(CRUD)操作を実行すると、クライアント登録CLIは登録アクセストークンを処理できなくなります。その場合、クライアントのために最も最近発行された登録アクセストークンの追跡を失う可能性があり、 manage-clients 権限を持つアカウントで認証なしでは、そのクライアントでそれ以上のCRUD操作を実行することは不可能になります。

権限を持っている場合は、クライアント用に新しい登録アクセストークンを発行し、標準出力に出力するか、選択した設定ファイルに保存することができます。それ以外の場合は、レルム管理者にクライアントの新しい登録アクセストークンを発行して、それを送信するよう依頼する必要があります。その際は、 --token オプションを使ってCRUDコマンドに渡すことができます。また、 kcreg config registration-token コマンドを使って新しいトークンを設定ファイルに保存し、クライアント登録CLIが自動的にそれを処理するようにすることもできます。

kcreg update-token コマンドの詳細については、 kcreg update-token --help コマンドを実行してください。

6.5. トラブルシューティング

  • Q:ログインすると、次のエラーが表示されます:Parameter client_assertion_type is missing [invalid_client].

    A:このエラーメッセージは、クライアントが Signed JWT のトークン・クレデンシャルで設定されていることを意味します。つまり、ログイン時に --keystore パラメータを使用する必要があります。

7. Token Exchange

Token Exchangeは、 テクノロジー・プレビュー であり、完全にはサポートされていません。この機能はデフォルトで無効になっています。

有効にするには、 -Dkeycloak.profile=preview を使用してサーバーを起動します。 または、 -Dkeycloak.profile.feature.token_exchange=enabled を使用してサーバーを起動します。 . 詳しくは、 Profiles を参照してください。

Token Exchangeを使用するには、 admin_fine_grained_authz 機能も有効にする必要があります。 Profiles を参照してください。

Keycloakにおいて、Token Exchangeとはクレデンシャルまたはトークンのセットを使用して、全く異なるトークンを取得するプロセスです。クライアントは信頼性の低いアプリケーションを呼び出すかもしれず、現在のトークンをダウングレードしたい場合があります。クライアントは、リンクされたソーシャル・プロバイダー・アカウントのために保存されたトークンを、Keycloakトークンに交換したいことがあります。他のKeycloakのレルムや外部IDPによって作成された外部トークンを信頼することができます。クライアントはあるユーザーに成り代わる必要があるかもしれません。Token Exchangeに関するKeycloakの現在の機能の概要を簡単に説明します。

  • クライアントは、特定のクライアントに対して作成された既存のKeycloakトークンを、別のクライアントをターゲットとする新しいトークンと交換することができます

  • クライアントは、既存のKeycloakトークンを外部トークン(つまり、リンクされたFacebookアカウント)と交換できます

  • クライアントは、外部トークンをKeycloakトークンに交換できます。

  • クライアントは、ユーザーに成り代わることができます。

KeycloakのToken Exchangeは、 OAuth Token Exchange の仕様の非常にルーズな実装です。Keycloakでは、それを少し拡張し、一部を無視し、仕様の他の部分をルーズに解釈しました。これは、あるレルムのOpenID Connectトークン・エンドポイントでの単純なグラントタイプの呼び出しです。

/auth/realms/{realm}/protocol/openid-connect/token

フォーム・パラメーター( application/x-www-form-urlencoded )を入力として受け取り、出力は交換をリクエストしたトークンのタイプに依存します。Token Exchangeはクライアント・エンドポイントであるため、リクエストは呼び出し元のクライアントに認証情報を提供する必要があります。パブリック・クライアントは、クライアント識別子をフォーム・パラメーターとして指定します。コンフィデンシャル・クライアントは、フォーム・パラメーターを使用してクライアントIDとシークレット、BASIC認証を渡すこともできますが、管理者はレルム内でクライアント認証フローを設定することもできます。 フォーム・パラメーターのリストは次のとおりです

client_id

REQUIRED MAYBE 。このパラメーターは、認証にフォーム・パラメーターを使用するクライアントに必要です。BASIC認証、クライアントJWTトークン、またはクライアント証明書認証を使用している場合は、このパラメーターを指定しないでください。

client_secret

REQUIRED MAYBE 。 このパラメーターは、認証にフォーム・パラメーターを使用し、クレデンシャルとしてクライアント・シークレットを使用するクライアントに必要です。レルム内のクライアント呼び出しが別の方法で認証されている場合は、このパラメータを指定しないでください。

grant_type

REQUIRED 。パラメータの値は urn:ietf:params:oauth:grant-type:token-exchange でなければなりません。

subject_token

OPTIONAL 。リクエストを送信したユーザーの代わりのパーティのアイデンティティーを表すセキュリティー・トークンです。既存のトークンを新しいトークンと交換する場合に必要です。

subject_issuer

OPTIONALsubject_token の発行者を識別します。トークンが現在のレルムによるものの場合、または発行者が subject_token_type から決定できる場合は、空白のままにすることができます。それ以外の場合は、指定する必要があります。有効な値は、レルムに設定された Identity Provider のエイリアスです。または、特定の Identity Provider によって設定された発行者クレーム識別子です。

subject_token_type

OPTIONAL 。このパラメーターは subject_token パラメーターで渡されるトークンのタイプです。 subject_token がレルムのものでアクセストークンである場合、これはデフォルトで urn:ietf:params:oauth:token-type:access_token になります。それが外部トークンである場合、このパラメーターは subject_issuer の要件に応じて、指定する必要がある場合とない場合があります。

requested_token_type

OPTIONAL 。このパラメーターは、クライアントが交換したいトークンのタイプを表します。現在、トークンタイプとしてOAuthとOpenID Connectのみがサポートされています。デフォルト値は、これが urn:ietf:params:oauth:token-type:refresh_token であるかどうかによって決まります。この場合、レスポンス内でアクセストークンとリフレッシュトークンの両方が返されます。その他の適切な値は urn:ietf:params:oauth:token-type:access_tokenurn:ietf:params:oauth:token-type:id_token です。

audience

OPTIONAL 。このパラメーターには、新しいトークンを作成する対象となるクライアントを指定します。

requested_issuer

OPTIONAL 。このパラメーターは、クライアントが外部プロバイダーによって作成されたトークンを必要とすることを指定します。これは、レルム内で設定された Identity Provider のエイリアスでなければなりません。

requested_subject

OPTIONAL 。クライアントが別のユーザーに成り代わる場合は、ユーザー名またはユーザーIDを指定します。

scope

NOT IMPLEMENTED 。このパラメーターは、クライアントが要求しているOAuthおよびOpenID Connectのスコープのセットを表します。現時点では実装されていませんが、Keycloakがスコープの一般的なサポートを強化した後に利用可能になります。

現在のところ、OpenID ConnectとOAuthの交換のみサポートしています。SAMLベースのクライアントおよびアイデンティティー・プロバイダーのサポートは、将来、ユーザーの要求に応じて追加される可能性があります。

トークン交換の呼び出しの成功レスポンスは、クライアントがリクエストする requested-token-typerequested_issuer に依存したコンテンツタイプとともにHTTP 200レスポンスコードを返します。OAuthでリクエストされたトークンタイプの場合は、 OAuth Token Exchange の仕様に記載されているJSONドキュメントを返します。

{
   "access_token" : ".....",
   "refresh_token" : ".....",
   "expires_in" : "...."
 }

リフレッシュトークンを要求するクライアントは、レスポンス内のアクセストークンとリフレッシュトークンの両方を取得し直します。アクセストークン・タイプのみを要求するクライアントは、レスポンス内のアクセストークンを取得します。 requested_issuer パラメーターを介して外部発行者を要求するクライアントに対して、有効期限情報が含まれる場合と含まれない場合があります。

エラーレスポンスは一般的に400 HTTPレスポンス・コード・カテゴリーに該当しますが、エラーの重大度に応じて他のエラー・ステータスコードが返されることがあります。エラーレスポンスには、 requested_issuer に応じた内容が含まれます。OAuthベースのトークン交換では、次のようにJSON文書が返ることがあります。

{
   "error" : "...."
   "error_description" : "...."
}

トークン交換のタイプによっては追加のエラークレームが返されることがあります。たとえば、ユーザーがアイデンティティー・プロバイダーへのリンクを持っていない場合、OAuthアイデンティティー・プロバイダーは追加の account-link-url クレームを含めることがあります。このリンクは、クライアントが開始したリンクリクエストに使用できます。

Token Exchangeのセットアップには細かい粒度の管理権限が必要です(詳細はServer Administration Guideのリンクを参照してください)。クライアントに交換の権限を付与する必要があります。これについては、この章の後半で説明します。

この章では、セットアップの要件について説明し、さまざまな交換のシナリオの例を示します。簡単にするために、現在のレルムで作成されたトークンを 内部 トークン、外部レルムまたはアイデンティティー・プロバイダーが作成したトークンを 外部 トークンとして呼ぶことにします。

7.1. 内部トークンから内部トークンへの交換

内部トークンから内部トークンへの交換で、特定のクライアントに発行された既存のトークンを、別のターゲット・クライアント用に作成された新しいトークンと交換する必要があります。なぜこれをしたいのでしょうか?これは一般に、クライアントはトークンを持っているが、別のアクセストークンのクレームと権限を必要とする他のアプリケーションに対して、追加のリクエストを行う必要がある場合に発生します。このタイプの交換が必要なその他の理由は、信頼性の低いアプリケーションでアプリケーションを呼び出す必要があり、現在のアクセストークンを伝播したくない場合に"権限のダウングレード"を実行する必要がある場合です。

7.1.1. 交換のための権限を付与

別のクライアントとトークンを交換したいクライアントは、管理コンソールで認可される必要があります。交換の権限を与えたいターゲット・クライアントに token-exchange のきめ細かい権限を定義する必要があります。

ターゲット・クライアントの権限

exchange target client permission unset

Permissions Enabled スイッチをONに切り替えます。

ターゲット・クライアントの権限

exchange target client permission set

このページに token-exchange のリンクがあります。クリックすると、権限の定義が開始され、このページに移動します。

ターゲット・クライアントの交換権限のセットアップ

exchange target client permission setup

この権限に対するポリシーを定義する必要があります。 Authorization のリンクをクリックし、 Policies タブに行き、 Client ポリシーを作成してください。

クライアント・ポリシーの作成

exchange target client policy

ここでは、開始するクライアント、つまりトークン交換を要求している認証済みクライアントを入力します。このポリシーを作成したら、ターゲット・クライアントの token-exchange の権限に戻り、今定義したクライアント・ポリシーを追加します。

クライアント・ポリシーの適用

exchange target client exchange apply policy

これでクライアントは交換を実行する権限を持ちます。これを正しく行わないで交換しようとすると、403 Forbiddenのレスポンスが表示されます。

7.1.2. リクエストの実行

クライアントがターゲットとする別のクライアントのトークンを既存のトークンと交換しているときは、 audience パラメーターを使用する必要があります。このパラメーターは、管理コンソールで設定したターゲット・クライアントのクライアント識別子である必要があります。

curl -X POST \
    -d "client_id=starting-client" \
    -d "client_secret=the client secret" \
    --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
    -d "subject_token=...." \
    --data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:refresh_token" \
    -d "audience=target-client" \
    http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token

subject_token パラメーターは、ターゲット・レルムのアクセストークンでなければなりません。 requested_token_type パラメーターがリフレッシュトークン・タイプの場合、レスポンスにはアクセストークン、リフレッシュトークン、および有効期限の全てが含まれます。この呼び出しから返されるJSONレスポンスの例を、次に示します。

{
   "access_token" : "....",
   "refresh_token" : "....",
   "expires_in" : 3600
}

7.2. 内部トークンから外部のトークンへの交換

レルムのトークンを、外部アイデンティティー・プロバイダーによって作成された外部トークンに交換することができます。この外部アイデンティティー・プロバイダーは、管理コンソールの Identity Provider セクション内で設定する必要があります。現在のところ、OAuth/OpenID Connectベースの外部アイデンティティー・プロバイダーのみがサポートされています。これには、すべてのソーシャル・プロバイダーが含まれます。Keycloakは、外部プロバイダーへのバックチャネル交換を実行しません。したがって、アカウントがリンクされていない場合、外部トークンを取得することはできません。外部トークンを取得するには、次のいずれかの条件を満たす必要があります。

  • ユーザーは、外部アイデンティティー・プロバイダーに少なくとも1回はログインしている必要があります。

  • ユーザーは、外部アイデンティティー・プロバイダーにユーザー・アカウント・サービスを介してリンクしている必要があります。

  • ユーザー・アカウントは、Client Initiated Account Linking APIを使用して外部アイデンティティー・プロバイダーを介してリンクされています。

最後に、外部アイデンティティー・プロバイダーがトークンを保存するように設定されている必要があります。または、上記のアクションの1つが、交換する内部トークンと同じユーザー・セッションで実行されている必要があります。

アカウントがリンクされていない場合、交換レスポンスにはリンクの確立に使用できるリンクが含まれます。これについては、リクエストの実行のセクションで詳しく説明します。

7.2.1. 交換のための権限を付与

呼び出し側のクライアントに外部アイデンティティー・プロバイダーとトークンを交換する権限を与えるまで、内部トークンから外部トークンへの交換リクエストは、403 Forbiddenレスポンスで拒否されます。クライアントに権限を与えるには、アイデンティティー・プロバイダーの設定ページの Permissions タブに移動する必要があります。

アイデンティティー・プロバイダーの権限

exchange idp permission unset

Permissions Enabled スイッチをtrueに切り替えます。

アイデンティティー・プロバイダーの権限

exchange idp permission set

このページに token-exchange のリンクがあります。クリックすると、権限の定義が開始され、このページに移動します。

アイデンティティー・プロバイダーの交換権限のセットアップ

exchange idp permission setup

この権限に対するポリシーを定義する必要があります。 Authorization のリンクをクリックし、 Policies タブに行き、 Client ポリシーを作成してください。

クライアント・ポリシーの作成

exchange idp client policy

ここでは、開始するクライアント、つまりトークン交換を要求している認証済みクライアントを入力します。このポリシーを作成したら、アイデンティティー・プロバイダーの token-exchange の権限に戻り、今定義したクライアント・ポリシーを追加します。

クライアント・ポリシーの適用

exchange idp apply policy

これでクライアントは交換を実行する権限を持ちます。これを正しく行わないで交換しようとすると、403 Forbiddenのレスポンスが表示されます。

7.2.2. リクエストの実行

クライアントが既存の内部トークンを外部トークンと交換する場合は、 requested_issuer パラメーターを指定する必要があります。パラメーターは、設定済みのアイデンティティー・プロバイダーのエイリアスでなければなりません。

curl -X POST \
    -d "client_id=starting-client" \
    -d "client_secret=the client secret" \
    --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
    -d "subject_token=...." \
    --data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:access_token" \
    -d "requested_issuer=google" \
    http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token

subject_token パラメーターは、ターゲット・レルムのアクセス・トークンでなければなりません。 requested_token_type パラメーターは、 urn:ietf:params:oauth:token-type:access_token またはブランクのままでなければなりません。現時点では、それ以外の要求されたトークン・タイプはサポートされていません。この呼び出しから返される成功のJSONレスポンスの例を次に示します。

{
   "access_token" : "....",
   "expires_in" : 3600
   "account-link-url" : "https://...."
}

外部アイデンティティー・プロバイダーが何らかの理由でリンクされていない場合は、このJSONドキュメントでHTTP 400レスポンス・コードが返ります。

{
   "error" : "....",
   "error_description" : "..."
   "account-link-url" : "https://...."
}

error クレームは、 token_expirednot_linked のどちらかになります。 account-link-url クレームは、クライアントがClient Initiated Account Linkingを実行できるように提供されています。ほとんど(すべて?)のプロバイダーは、ブラウザーOAuthプロトコルを介してリンクする必要があります。 account-link-url では、 redirect_uri クエリー・パラメーターを追加するだけで、ブラウザーを転送してリンクを実行することができます。

7.3. 外部トークンから内部トークンへの交換

外部アイデンティティー・プロバイダーが発行した外部トークンを信頼して、内部トークンに交換することができます。これは、レルム間のブリッジや、ソーシャル・プロバイダーのトークンの信頼に使用できます。これは、アイデンティティー・プロバイダーのブラウザーのログインと同様に動作します。新しいユーザーが存在しない場合、そのユーザーはレルムにインポートされます。

外部トークン交換の現在の制限は、外部トークンが既存のユーザーにマップされている場合、既存のユーザーに外部アイデンティティー・プロバイダーへのアカウント・リンクがない限り、交換は許可されないということです。

交換が完了すると、レルム内にユーザー・セッションが作成され、 requested_token_type パラメーター値に応じて、アクセストークンまたはリフレッシュトークンを受け取れます。この新しいユーザー・セッションは、タイムアウトするまで、またはこの新しいアクセストークンを渡すレルムのログアウト・エンドポイントを呼び出すまで、アクティブなままであることに注意してください。

これらのタイプの変更には、管理コンソールで設定されたアイデンティティー・プロバイダーが必要でした。

現時点では、SAMLアイデンティティー・プロバイダーはサポートされていません。Twitterのトークンも交換できません。

7.3.1. 交換のための権限を付与

外部トークンの交換が可能になる前に、発信側のクライアントが交換を行うための権限を与える必要があります。この権限は、内部トークンから外部トークンへの交換権限の付与と同じ方法で付与されます。

値が呼び出し側以外の別のクライアントを指し示す audience パラメーターも指定した場合、呼び出し側クライアントに audience パラメーターで特定のターゲット・クライアントに交換する権限を与える必要があります。これを行う方法については、このセクションの前半で説明しています。

7.3.2. リクエストの実行

subject_token_type は、 urn:ietf:params:oauth:token-type:access_token または urn:ietf:params:oauth:token-type:jwt のいずれかでなければなりません。タイプが urn:ietf:params:oauth:token-type:access_token の場合、 subject_issuer パラメーターを指定しなければなりません。また、設定されたアイデンティティー・プロバイダーのエイリアスでなければなりません。タイプが urn:ietf:params:oauth:token-type:jwt の場合、プロバイダーは、プロバイダーのエイリアスであるJWT内の issuer クレームまたは、プロバイダー設定内の登録された発行者と一致します。

トークンがアクセストークンである場合、プロバイダーのUserInfoサービスが呼び出されてトークンを検証します。呼び出しが成功することは、アクセストークンが有効であることを意味します。サブジェクト・トークンがJWTであり、プロバイダーが署名検証を有効にしている場合は、それが試行され、そうでない場合は、デフォルトでトークンを検証するためにUserInfoサービスが呼び出されます。

デフォルトでは、発行された内部トークンは、呼び出し元クライアント用に定義されたプロトコル・マッパーを使用してトークンの内容を判断するため、呼び出し元クライアントを使用します。あるいは、 audience パラメーターを使って別のターゲット・クライアントを指定することもできます。

curl -X POST \
    -d "client_id=starting-client" \
    -d "client_secret=the client secret" \
    --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
    -d "subject_token=...." \
    -d "subject_issuer=myOidcProvider" \
    --data-urlencode "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
    -d "audience=target-client" \
    http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token

requested_token_type パラメーターがリフレッシュトークン・タイプの場合、レスポンスにはアクセストークン、リフレッシュトークン、および有効期限の両方が含まれます。この呼び出しで返されるJSONレスポンスの例を次に示します。

{
   "access_token" : "....",
   "refresh_token" : "....",
   "expires_in" : 3600
}

7.4. Impersonation

内部トークン交換と外部トークン交換の場合、クライアントはユーザーの代わりに別のユーザーに成り代わるように要求できます。たとえば、サポート・エンジニアが問題をデバッグできるように、ユーザーに成り変わる必要がある管理アプリケーションがあるとします。

7.4.1. 交換のための権限を付与

サブジェクト・トークンが表すユーザーには、他のユーザーに成り代わる権限が必要です。この権限を有効にする方法については、Server Administration Guideを参照してください。ロールを通じて、またはきめ細かい管理権限を使って行うことができます。

7.4.2. リクエストの実行

追加で requested_subject パラメーターを指定している点を除いて、他の章で説明したようにリクエストを行います。このパラメーターの値は、ユーザー名またはユーザーIDでなければなりません。

curl -X POST \
    -d "client_id=starting-client" \
    -d "client_secret=the client secret" \
    --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
    -d "subject_token=...." \
    --data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:access_token" \
    -d "audience=target-client" \
    -d "requested_subject=wburke" \
    http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token

7.5. ダイレクト・ネイキッドな成り代わり

subject_token を指定せずに、内部トークンの交換要求を行うことができます。これは、ダイレクト・ネイキッドな成り代わりと呼ばれます。クライアントがそのレルム内の任意のユーザーに成り代わる可能性があるため、クライアントに多くの信頼を置くからです。交換するサブジェクト・トークンを取得することが不可能なアプリケーションをブリッジするためにこれが必要となるかもしれません。たとえば、LDAPに直接ログインするレガシー・アプリケーションを統合したいかもしれません。その場合、従来のアプリケーションはユーザー自身を認証できますが、トークンを取得することができません。

クライアントにダイレクト・ネイキッドな成り代わりを有効にすることは非常に危険です。クライアントのクレデンシャルが盗まれた場合、そのクライアントはシステム内のすべてのユーザーに成り代わる可能性があります。

7.5.1. 交換のための権限を付与

audience パラメーターが提供されている場合、呼び出し側クライアントはクライアントとの交換権限を持っていなければなりません。これを設定する方法については、この章の前半で説明します。

さらに、呼び出し元のクライアントにユーザーに成り代わる権限が与えられている必要があります。管理コンソールで、 Users 画面を表示し、 Permissions タブをクリックしてください。

ユーザーの権限

exchange users permission unset

Permissions Enabled スイッチをtrueに切り替えます。

アイデンティティー・プロバイダーの権限

exchange users permission set

ページに impersonation リンクがあります。クリックすると、権限の定義が開始され、このページに移動します。

ユーザー成り代わり権限のセットアップ

exchange users permission setup

この権限に対するポリシーを定義する必要があります。 Authorization のリンクをクリックし、 Policies タブに行き、 Client ポリシーを作成してください。

クライアント・ポリシーの作成

exchange users client policy

ここでは、開始するクライアント、つまりトークン交換を要求している認証済みクライアントを入力します。このポリシーを作成したら、ユーザーの impersonation 権限に戻り、今定義したクライアント・ポリシーを追加します。

クライアント・ポリシーの適用

exchange users apply policy

クライアントにユーザーに成り代わる権限が付与されました。これを正しく行わないと、これを交換のタイプにしようとする際に、403 Forbiddenのレスポンスが表示されます。

パブリック・クライアントはダイレクト・ネイキッドな成り代わりを行うことができません。

7.5.2. リクエストの実行

リクエストを行うには、 requested_subject パラメーターを指定してください。これは、有効なユーザーのユーザー名またはユーザーIDでなければなりません。また、必要に応じて audience パラメーターを指定することもできます。

curl -X POST \
    -d "client_id=starting-client" \
    -d "client_secret=the client secret" \
    --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
    -d "requested_subject=wburke" \
    http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token

7.6. サービス・アカウントの権限モデルの拡張

クライアントに交換の権限を付与する際に、クライアントごとに権限を手動で有効にする必要はありません。クライアントに関連付けられているサービス・アカウントがある場合は、ロールを使用して権限をグループ化し、クライアントのサービス・アカウントにロールを割り当てて、交換の権限を割り当てることができます。たとえば、 naked-exchange ロールを定義し、そのロールを持つサービス・アカウントでネイキッドな交換を行うことができます。

7.7. 交換の脆弱性

Token Exchangeの許可を行う上で、意識して気をつけなければならないさまざまなことがあります。

1つはパブリック・クライアントです。パブリック・クライアントは、交換を実行するためにクライアント・クレデンシャルを持たない、または必要としません。有効なトークンを持っていれば、パブリック・クライアントに偽装することができ、パブリック・クライアントに実行が許可されている交換を実行できます。レルムで管理されている信頼できないクライアントがある場合、パブリック・クライアントは許可モデルで脆弱性を公開する可能性があります。このため、ダイレクト・ネイキッド交換ではパブリック・クライアントは許可されず、呼び出し元のクライアントがパブリックの場合は、エラーで中止されます。

FacebookやGoogleなどが提供するソーシャル・トークンをレルム・トークンと交換することが可能です。これらのソーシャル・ウェブサイト上で偽のアカウントを作成するのが難しくないため、交換するトークンで何ができるのかを注意して自警してください。デフォルトのロール、グループ、およびアイデンティティー・プロバイダーのマッパーを使用して、外部ソーシャル・ユーザーに割り当てられる属性とロールを制御します。

ダイレクト・ネイキッド交換は非常に危険です。呼び出し元のクライアントに対して、クライアントのクレデンシャルが漏洩しないということを非常に信頼しています。これらのクレデンシャルが漏洩した場合、攻撃者はシステム内の誰かに偽装する可能性があります。これは、既存のトークンを持つコンフィデンシャル・クライアントとはまったく対照的です。2要素の認証(アクセストークンとクライアントのクレデンシャル)がありますが、1ユーザーしか扱っていません。したがって、ダイレクト・ネイキッド交換は控えめに使用してください。