第12章 トークン交換の設定と使用
Red Hat build of Keycloak のトークン交換を設定して使用します。
トークン交換は、クライアントアプリケーションが別のトークンと交換できるようにするプロセスです。Red Hat build of Keycloak では、次の 2 つの機能によってトークン交換が実装されます。
- 標準トークン交換: バージョン 2 (V2) - この機能は、Red Hat build of Keycloak サーバーが起動するとデフォルトで有効になるトークン交換の実装で、完全にサポートされています。
- レガシートークン交換: バージョン 1 (V1) - Red Hat build of Keycloak サーバーの起動後は、このプレビュー機能はデフォルトで有効になっていません。
トークン交換用の Red Hat build of Keycloak の機能は次のとおりです。
- クライアントが特定のクライアント用に作成された既存の Red Hat build of Keycloak トークンを、同じレルム内の別のクライアントを対象とする新しいトークンと交換できる。
- クライアントが、既存の Red Hat build of Keycloak トークンを外部トークン (リンクされた Facebook アカウントなど) と交換できる。
- クライアントが外部トークンを Red Hat build of Keycloak トークンと交換できる。
- クライアントが特定のユーザーとして振る舞うことができる。
標準トークン交換はユースケース (1) のみをサポートします。従来のトークン交換は 4 つのユースケースをサポートしていますが、これはプレビュー機能です。したがって、標準トークン交換 V2 はサポートされており、今後も維持されるため、推奨されます。レガシートークン交換は最後の 3 つのユースケースでは役立ちますが、今後の Red Hat build of Keycloak バージョンとの下位互換性がない可能性があります。両方のトークン交換機能を有効にして、一緒に使用することもできます。たとえば、V2 で提供される内部間交換と、V1 でサポートされている他のユースケースの両方を使用できます。詳細は、この トークン交換の比較 を参照してください。
レガシートークン交換機能がまだ必要な場合は、バージョン 2 (FGAP:v2) では、トークン交換権限がサポートされていないため、Fine-grained admin permissions バージョン 1 (FGAP:v1) も有効にする必要があります。これは意図的なものであり、トークン交換は概念的には実際には "admin" 権限ではないため、トークン交換権限を FGAP:v2 に追加する予定はありません。
12.1. 標準トークン交換 リンクのコピーリンクがクリップボードにコピーされました!
Red Hat build of Keycloak の標準トークン交換は トークン交換仕様 を実装します。これにより、クライアントアプリケーションは、特定のクライアント用に作成された既存の Red Hat build of Keycloak トークンを、トークン交換要求をトリガーしたクライアントに発行された新しいトークンと交換できるようになります。いずれのクライアントも同じレルムに存在する必要があります。
12.1.1. トークン交換フロー リンクのコピーリンクがクリップボードにコピーされました!
次の典型的なトークン交換フローを検討してください。
-
ユーザーは、Red Hat build of Keycloak SSO を使用してクライアントアプリケーション
initial-clientに対して認証します。トークンはinitial-clientに対して発行されます。 -
クライアント
initial-clientは、認証を必要とする REST サービスrequester-clientを使用する必要がある場合があります。そこで、initial-clientは、ステップ 1 のアクセストークンを使って、このアクセストークンをrequester-clientに送信します。 リクエストを処理するには、
requester-clientが別のサービスtarget-clientを呼び出す必要がある場合があります。ただし、initial-clientから送信されたトークンを使用できない可能性があります。以下に例を示します。- トークンの権限またはスコープが不十分です。
-
target-clientは、トークンのオーディエンスとして指定されていません。トークンはrequester-clientを呼び出すために使用されることになっています。 トークンには権限が多すぎるため、
requester-clientがすべての権限をtarget-clientと共有しないようにすることがあります。これらの状況のいずれも、トークン交換を呼び出す理由となる可能性があります。
requester-clientは、トークン交換要求を Red Hat build of Keycloak サーバーに送信し、ステップ 1 の元のトークンを サブジェクトトークン として使用して、それを 要求された 別のトークンと交換する必要がある場合があります。
-
要求されたトークン は
requester-clientに返されます。このトークンはtarget-clientに送信できるようになりました。 -
target-clientは、要求を実行し、requester-clientに応答を返すことができます。その後、requester-clientは、ステップ 2 からの要求に従って応答を返すことができます。
トークン交換には他にも多くのユースケースがありますが、前述の例が最も典型的です。
12.1.1.1. トークン交換リクエストの例 リンクのコピーリンクがクリップボードにコピーされました!
以下は、レルム test 内のクライアント requester-client のトークン交換リクエストの例です。subject_token は、initial-client に発行されたアクセストークンであることに注意してください。
POST /realms/test/protocol/openid-connect/token
Authorization: Basic cmVxdWVzdGVyLWNsaWVudDpwYXNzd29yZA==
Content-Type: application/x-www-form-urlencoded
Accept: application/json
grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
subject_token=$SUBJECT_TOKEN&
subject_token_type=urn:ietf:params:oauth:token-type:access_token&
requested_token_type=urn:ietf:params:oauth:token-type:access_token
トークン交換応答の例は次のようになります。
{
"access_token": "eyJhbGciOiJSUzI1NiIsIn...",
"expires_in": 300,
"token_type": "Bearer",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"session_state": "287f3c57-32b8-4c0f-8b00-8c7db231d701",
"scope": "default-scope1",
"refresh_expires_in": 0,
"not-before-policy": 0
}
12.1.2. トークン交換を有効にする方法 リンクのコピーリンクがクリップボードにコピーされました!
標準トークン交換の場合、token-exchange-standard:v2 がデフォルトで有効になっています。ただし、前の例 の requester-client など、トークン交換リクエストを送信することになっているクライアントに対しても、標準トークン交換 スイッチを有効にする必要があります。requester-client は、機密クライアントである必要があることに注意してください。また、他の付与要求の場合と同様に、トークン交換要求は、クライアントに設定されている適切な クライアント認証方法 によって認証される必要があります。
図12.1 トークン交換の有効化
12.1.3. 要求と応答のパラメーター リンクのコピーリンクがクリップボードにコピーされました!
パラメーターは Token exchange specification に準拠しており、次のように説明されています。
- grant_type
-
必須。パラメーターの値は
urn:ietf:params:oauth:grant-type:token-exchangeにする必要があります。 - subject_token
- 必須。リクエストが行われている当事者の ID を表すセキュリティートークン。
- subject_token_type
-
必須。このパラメーターは、
subject_tokenパラメーターで渡されるトークンのタイプです。標準トークン交換が使用されている場合、これはurn:ietf:params:oauth:token-type:access_tokenである必要があります。これは、Red Hat build of Keycloak が他のタイプの標準トークン交換をサポートしていないためです。 - requested_token_type
-
任意。このパラメーターは、クライアントが交換するトークンのタイプを表します。このバージョンでは、oauth および OpenID Connect トークンタイプのみがサポートされます。デフォルト値は
urn:ietf:params:oauth:token-type:access_tokenです。requester-clientに発行された ID トークンが要求される場合、使用可能な別の値はurn:ietf:params:oauth:token-type:id_tokenです。urn:ietf:params:oauth:token-type:refresh_tokenも値として使用できる可能性があります。この場合、応答内でアクセストークンとリフレッシュトークンの両方を受け取ります。ただし、標準トークン交換 セクションで指定されているようにAllow refresh token in Standard Token Exchangeのクライアント設定オプションが有効になっている場合は、リフレッシュトークンが許可されます。 - scope
-
任意。このパラメーターは、クライアントが要求している OAuth および OpenID Connect スコープのスペース区切りのセットを表します。
requester-clientの オプションのクライアントスコープ を使用できます。詳細は、スコープとオーディエンス を参照してください。このパラメーターを省略すると、デフォルトのクライアントスコープ のみが効果的に使用されます。 - audience
-
任意。Audience は、トークンオーディエンスとして使用されるクライアントの
client_idを指定します。上記の例 では、target-clientです。このパラメーターには複数の値が許可されているため、複数の異なるサービスでrequester-clientが使用する複数のオーディエンスをトークンに含めることができます。たとえば、リクエストではaudience=target-client1&audience=target-client2を使用できます。詳細は スコープとオーディエンスに関するセクション を参照してください。
成功した応答は JSON 形式で返されます。他のグラントからの応答など、同様のパラメーターが含まれます。以下で、トークン交換関連の主要なパラメーターを他にも挙げています。
- access_token
-
要求されたアクセストークン。リクエストで
requested_token_type=urn:ietf:params:oauth:token-type:id_tokenが指定された場合、このパラメーターには実際にはアクセストークンではなく ID トークンが含まれる可能性があることに注意してください。この動作は token exchange specification に準拠しています。 - refresh_token
-
リフレッシュトークン。これは
requested_token_type=urn:ietf:params:oauth:token-type:refresh_tokenが使用され、クライアントがトークン交換からリフレッシュトークンの発行を有効にしている場合にのみ含まれます。 - issued_token_type
-
発行された要求されたトークンの種類。リクエストで使用される
requested_token_typeと同じ値。 - token_type
-
発行されたトークンタイプがアクセストークンまたはリフレッシュトークンの場合は、通常は
Bearerです。ID トークンが要求された場合、値はN_Aです
12.1.4. スコープとオーディエンス リンクのコピーリンクがクリップボードにコピーされました!
トークン交換リクエストの scope パラメーターは、他のグラントと同じ意味を持ちます。このパラメーターは任意です。省略した場合、リクエストで使用される有効なクライアントスコープは requester-client の デフォルトのクライアントスコープ です。このパラメーターを使用すると、有効なクライアントスコープは、デフォルトのスコープと オプションのクライアントスコープ です。
デフォルトでは、使用されているクライアントスコープとクライアントロールに基づいて、Audience ドキュメント で指定されているとおりに、トークンの aud クレームにオーディエンスが追加されます。
audience パラメーターはオーディエンスのフィルタリングに使用できます。これにより、aud クレームには、audience パラメーターによって指定されたオーディエンスのみが含まれます。同様に、トークン内のクライアントロールはフィルタリングされ、トークンには、audience パラメーターで指定されたクライアントのクライアントロールのみが含まれます。
さらに、audience パラメーターを使用して、クライアントスコープをフィルタリングすることもできます。これは ユーザーに対するクライアントスコープのアクセス許可 と同様の方法で動作します。クライアントスコープにクライアントロールが含まれていない場合 (たとえば、ロールが 0 個含まれているか、レルムロールのみ含まれている場合)、クライアントスコープに対して追加のフィルタリングは行われません。ただし、クライアントスコープにクライアントロールのマッピングが含まれている場合は、audience パラメーターでリクエストされたクライアントに、そのクライアントロールの一部が含まれている必要があります。複合ロールも考慮の対象となります。クライアントスコープに、audience によって要求されたクライアントのクライアントロールが含まれていない場合、クライアントスコープはフィルターされます。
audience パラメーターを使用すると、使用されているクライアントスコープから取得されるオーディエンスをフィルタリングできます。ただし、このパラメーターによってオーディエンスがさらに追加されることはありません。audience パラメーターを省略すると、フィルタリングは行われません。その結果、audience パラメーターは、トークンを「ダウンスコープ」して、要求されたオーディエンスのみが含まれるようにするために効果的に使用されます。ただし、scope パラメーターはオプションのクライアントスコープの追加に使用されるため、このパラメーターで「アップスコープ」してさらにスコープを追加できます。
12.1.4.1. 例 リンクのコピーリンクがクリップボードにコピーされました!
スコープとオーディエンスの動作をよりわかりやすく説明するための例をいくつか示します。
次のようなレルムがあると仮定します。
-
target-client1-roleクライアントロールを持つtarget-client1クライアント -
target-client2-roleクライアントロールを持つtarget-client2クライアント -
target-client3-roleクライアントロールを持つtarget-client3クライアント -
default-scope1クライアントスコープこのクライアントスコープには、クライアントロールtarget-client1/target-client1-roleのロールスコープマッピングがあります。 -
optional-scope2クライアントスコープ。このクライアントスコープには、クライアントロールtarget-client2/target-client2-roleのロールスコープマッピングがあります。 -
クライアント
requester-clientには、デフォルトのクライアントスコープとしてクライアントスコープdefault-scope1が追加され、オプションのクライアントスコープとしてスコープoptional-scope2が追加されています。 -
target-client1-roleとtarget-client2-roleの両方のメンバーである認証済みユーザー
上記の設定は、スコープ default-scope1 を使用すると、オーディエンス target-client1 がトークンに追加され、optional-scope2 を使用すると、オーディエンス target-client2 が追加されます。これは、Audience ドキュメント で説明されているオーディエンス解決によるものです。
12.1.4.1.1. 例 1 リンクのコピーリンクがクリップボードにコピーされました!
scope=optional-scope2 で、audience パラメーターなしで送信されたトークン交換リクエスト:
audience のフィルタリングは行われません。スコープとオーディエンスは、クライアントスコープ と オーディエンスのドキュメント セクションで説明されているように、他のグラントの場合と同様に解決されます。応答トークンは次のようになります (この例では、簡素化するために、関係のないクレームは省略されています)。
{
"azp": "requester-client",
"scope": "default-scope1 optional-scope2",
"aud": [ "target-client1", "target-client2" ],
"resource_access": {
"target-client1": {
"roles": [ "target-client1-role" ]
},
"target-client2": {
"roles": [ "target-client2-role" ]
}
},
...
}
12.1.4.1.2. 例 2 リンクのコピーリンクがクリップボードにコピーされました!
scope=optional-scope2 および audience=target-client2 でトークン交換リクエストを送信する
前の例と同じですが、target-client1 オーディエンスと、audience パラメーターによってフィルタリングされたクライアントロールが含まれていますが、これは target-client2 クライアントのみに当てはまります。クライアントスコープ default-scope1 も、いくつかのクライアントロールが含まれているためフィルター処理されますが、要求されたオーディエンスクライアント target-client2 のクライアントロールは含まれていません。トークンは次のとおりです。
{
"azp": "requester-client",
"scope": "optional-scope2",
"aud": [ "target-client2" ],
"resource_access": {
"target-client2": {
"roles": [ "target-client2-role" ]
}
},
...
}
12.1.4.1.3. 例 3 リンクのコピーリンクがクリップボードにコピーされました!
scope=optional-scope2 および audience=target-client2&audience=target-client3 でトークン交換リクエストを送信する
ユーザーにロールがないため、target-client3 はトークンオーディエンスの一部ではありません。したがって、この場合、要求されたオーディエンスの一部が利用できないため、要求は拒否されます。
トークン交換仕様で述べられているように、トークンの範囲を可能な限り狭め、必要なオーディエンスのみを使用することを推奨します。理想的には、使用するオーディエンスは 1 つにします。このストラテジーにより、リクエストが許可される可能性が高まります。
さまざまなスコープとオーディエンスが含まれる複雑なデプロイメントの場合、適切な方法でモデル化することが困難になる可能性があります。クライアントスコープの評価タブ を使用して、トークンが特定のユーザーおよび特定のスコープとオーディエンスのセットに対して期待どおりであるかどうかをテストすることを検討してください。
12.1.5. トークン交換 - 追加の詳細 リンクのコピーリンクがクリップボードにコピーされました!
これらの追加ポイントにより、トークン交換の動作が明確になります。
- パブリッククライアントによるトークン交換リクエストの送信はサポートされていません。V1 では、パブリッククライアントが自分自身に対してトークンを交換できましたが、パブリッククライアントに対するサポートは非常に限定的でした。このユースケースは、リフレッシュトークングラントに置き換えることができます。
-
トークン交換エンドポイントに送信される
subject_tokenでは、audクレームでリクエスタークライアントがオーディエンスとして設定されている必要があります。そうでない場合、リクエストは拒否されます。唯一の例外は、クライアントが自分に発行された独自のトークンを交換する場合です。トークンをダウンスコープ/アップスコープしたり、不要なトークンオーディエンスをフィルター処理したりするには、トークン自体を交換すると便利です。 - 同意 - 要求元クライアントの Consent required 設定が有効になっている場合は、ユーザーが要求されたすべてのスコープにすでに同意している場合にのみトークン交換が許可されます。
- 標準トークン交換には Fine-grained admin permissions (FGAP) は必要ありません。今後 FGAP と統合する予定ですが、この統合はすべてのグラントで利用できるとは限りません。トークン交換 V1 の場合のように、トークン交換にのみ固有のものではありません。
-
トークン交換を クライアントポリシー と統合することが可能です。この統合は、特定のユースケースに対処するのに役立ちます。たとえば、クライアント
requester-clientがscope=some-confidential-scopeでリクエストを送信した場合にトークン交換リクエストを拒否するユースケースを考えてみましょう。この例では、client-scope、grant-type、client-rolesの条件を組み合わせたクライアントポリシー条件を作成すると便利です。 -
リフレッシュトークンの要求は、クライアントのスイッチ Allow refresh token in Standard Token Exchange が
No(デフォルト値) 以外の値に設定されている場合にのみ許可されます。スイッチは、管理コンソールの OIDC クライアントの Advanced タブにある OpenID Connect Compatibility Modes セクションで使用できます。スイッチに使用可能な値として、他に Same session があります。これは、リフレッシュトークンがサブジェクトトークンと同じユーザーセッションを使用できる場合にのみ、リフレッシュトークンが許可されることを意味します。そのサブジェクトトークンが 一時セッション または オフラインセッション から取得した場合、要求するリフレッシュトークンは許可されません。同様に、オフライントークンを要求することもできません (scope=offline_accessを使用)。
図12.2 トークン交換でリフレッシュトークンを有効にする
-
トークン交換では新しい ユーザーセッション は作成されません。
requested_token_typeがリフレッシュトークンの場合、最終的には、要求元クライアントのユーザーセッション内に新しいクライアントセッションが作成されることがあります (クライアントセッションがまだ作成されていない場合)。 -
Red Hat build of Keycloak トークン交換では
resourceパラメーターはまだサポートされていません。 - トークン交換仕様では impersonation and delegation の概念について言及されています。Red Hat build of Keycloak は、impersonation のユースケースをサポートしていますが、委譲ユースケースはまだサポートしていません。
12.1.5.1. 失効 リンクのコピーリンクがクリップボードにコピーされました!
クライアント initial-client にサブジェクトトークン access-token1 が発行されていると仮定すると、トークンの失効に関連する考慮事項は次のとおりです。
-
access-token1がクライアントrequester-clientのaccess-token2に交換された場合、access-token1を取り消してもaccess-token2は取り消されません。アクセストークンの「revocation chain (失効チェーン)」をサポートする場合は、かなりのオーバーヘッドが発生します。したがって、これを考慮して、管理者はアクセストークンの有効期間が短く、一定時間が経過すると自動的に取り消されるようにする必要があります。 access-token1がクライアントrequester-clientのrefresh-token2に交換された場合、失効チェーンのサポートを試みます。これは以下を意味します。-
access-token1を取り消すとrefresh-token2も取り消されます。さらに、これにより、クライアントrequester-clientのクライアントセッションがユーザーセッションから削除され、このユーザーセッション内のrequester-clientのすべてのリフレッシュトークンが事実上取り消されます。 -
refresh-token2とそれに関連するアクセストークンが別のクライアントとのさらなるトークン交換に使用された場合、access-token1の失効により、それ以降のトークン交換も取り消されます。言い換えれば、交換されたトークンの「チェーン」全体が取り消されることになります。 -
失効エンドポイントが呼び出されるときに、アクセストークンが有効である必要があることに注意してください。元の
access-token1の有効期限が切れたときに有効なアクセストークンがない場合は、同じユーザーセッションで同じクライアントに発行された別のアクセストークンを使用できる可能性があります。「チェーン」からのrefresh-token2など、交換されたトークンは、取り消される必要があります。
-
12.1.6. 標準トークン交換とレガシートークン交換の比較 リンクのコピーリンクがクリップボードにコピーされました!
前のセクションでは、標準トークン交換とレガシートークン交換を詳しく説明しましたが、以下は 2 つのトークン交換方法を比較した全体的な概要です。
| 機能 | 標準トークン交換 V2 | レガシートークン交換 V1 |
|---|---|---|
| Internal-internal token exchange | サポート対象。RFC8693 に従って実装。 | プレビュー機能としてのサポート。rfc8693 の緩い実装。代わりに V2 を使用することを推奨します |
|
Allowed | アクセストークンタイプのみ | アクセストークンタイプは internal-internal のみ、external-internal のシナリオの場合は JWT |
|
Allowed | アクセストークン (デフォルト)、リフレッシュトークン、ID トークン | アクセストークン、リフレッシュトークン (デフォルト)、SAML2 アサーション |
|
| 他のグラントと連携します。scope パラメーターは、トークン交換リクエストを送信したクライアントのオプションのスコープを要求することを意味します。 | audience パラメーターによって指定された「ターゲット」クライアントのスコープに基づく scope パラメーター。ダウンスコープサポートのみ |
|
| 仕様に従って、より多くの値をサポートします。この機能を使用することで、利用可能なオーディエンスを絞り込み、要求されたオーディエンスのみを保持できます。必要なターゲットオーディエンスに応じてトークンを効果的に縮小します。 | 単一の audience 値のサポート。トークンは、audience パラメーターによって要求され、そのクライアントのスコープを使用してクライアントに効果的に発行されます。 |
| Public clients | 利用できません。V1 で実装されたダウンスコープはリフレッシュトークンのグラントに置き換えることができます。 | クライアント自身のトークンの交換にのみ利用可能です。実質的にダウンスコープのサポートのみです。 |
| Consents |
ユーザーがすでに同意を得ている限り、 | Consent required のクライアントには許可されません |
| 認可 |
要求元クライアントが | fine-grained admin permissions バージョン 1 に基づく |
| Revocation chain | アクセストークンには使用できません。リフレッシュトークンで利用可能 | アクセストークンやリフレッシュトークンには利用できません |
| RFC8693 準拠の委譲 | まだサポートされていません | サポート対象外 |
| RFC8693 準拠のリソースパラメーター | まだサポートされていません | サポート対象外 |
| Federated token exchange | まだ実装されていません | プレビュー機能として実装 |
| Subject impersonation (direct naked impersonation を含む) | まだ実装されていません | プレビュー機能として実装 |