トークン交換の使用

Keycloak でのトークン交換の設定と使用

トークン交換とは、クライアントアプリケーションがトークンを別のトークンと交換できるプロセスです。Keycloak では、トークン交換を実装する 2 つの機能があります。

Keycloak のトークン交換の機能は次のとおりです。

  1. クライアントは、特定のクライアント用に作成された既存の Keycloak トークンを、同じレルム内の別のクライアントを対象とした新しいトークンと交換できます。

  2. クライアントは、既存の Keycloak トークンを、リンクされた Facebook アカウントなどの外部トークンと交換できます。

  3. クライアントは、外部トークンを Keycloak トークンと交換できます。

  4. クライアントは、ユーザーを偽装できます。

標準トークン交換は、ユースケース (1) のみをサポートします。レガシートークン交換は、4 つのユースケースをサポートしますが、プレビュー機能です。したがって、標準トークン交換 V2 は、サポートされており、将来もメンテナンスされるため推奨されます。レガシートークン交換は、最後の 3 つのユースケースに役立ちますが、将来の Keycloak バージョンとの下位互換性がない可能性があります。両方のトークン交換機能を有効にして、一緒に使用することもできます。たとえば、V2 で提供される内部 - 内部交換と、V1 でサポートされる他のユースケースを組み合わせて使用​​できます。詳細については、トークン交換の比較を参照してください。

レガシートークン交換機能がまだ必要な場合は、きめ細かい管理者権限バージョン 1 (FGAP:v1) も有効にする必要があります。バージョン 2 (FGAP:v2) にはトークン交換権限のサポートがないためです。トークン交換は概念的には実際には「管理者」権限ではないため、トークン交換権限を FGAP:v2 に追加する計画はありません。これは意図的なものです。

標準トークン交換

Keycloak の標準トークン交換は、トークン交換仕様を実装しています。これにより、クライアントアプリケーションは、特定のクライアント用に作成された既存の Keycloak トークンを、トークン交換リクエストをトリガーしたクライアントに発行された新しいトークンと交換できます。両方のクライアントは同じレルム内にある必要があります。

トークン交換フロー

この典型的なトークン交換フローを考えてみましょう。

  1. ユーザーは、Keycloak SSO を使用してクライアントアプリケーション initial-client で認証します。トークンは initial-client に発行されます。

  2. クライアント initial-client は、認証を必要とする REST サービス requester-client を使用する必要がある場合があります。そのため、initial-client はステップ 1 のアクセストークンをトークンを使用して requester-client に送信します。

  3. リクエストを処理するために、requester-client は別のサービス target-client を呼び出す必要がある場合があります。ただし、initial-client から送信されたトークンを使用できない場合があります。例えば

    • トークンには、十分な権限またはスコープがありません。

    • target-client はトークンオーディエンスとして指定されていません。トークンは requester-client を呼び出すために使用されることを意図していました。

    • トークンには権限が多すぎます。したがって、requester-client はそれを target-client と共有したくない場合があります。

      これらの状況のいずれかが、トークン交換を呼び出す理由になる可能性があります。requester-client は、トークン交換リクエストを Keycloak サーバーに送信し、ステップ 1 の元のトークンをサブジェクトトークンとして使用して、別のトークンリクエストされたトークンと交換する必要がある場合があります。

  4. リクエストされたトークンrequester-client に返されます。このトークンは、target-client に送信できるようになりました。

  5. target-client はリクエストを処理し、応答を requester-client に返すことができます。次に、requester-client はステップ 2 からのリクエストに応答を返すことができます。

トークン交換には他の多くのユースケースがありますが、上記の例が最も典型的です。

トークン交換リクエストの例

以下は、レルム test 内のクライアント requester-client のトークン交換リクエストの例です。subject_tokeninitial-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
}

トークン交換を有効にする方法

標準トークン交換の場合、token-exchange-standard:v2 はデフォルトで有効になっています。ただし、前の例requester-client など、トークン交換リクエストを送信するクライアントに対して、標準トークン交換スイッチを有効にする必要もあります。requester-client はコンフィデンシャルクライアントである必要があることに注意してください。また、他のグラントリクエストの場合と同様に、トークン交換リクエストは、クライアントに構成されている適切なクライアント認証方法によって認証される必要があります。

Enabling Token Exchange
図 1. トークン交換の有効化

リクエストおよびレスポンスパラメータ

パラメータは、トークン交換仕様に合わせており、次のように説明されています。

grant_type

必須。パラメータの値は urn:ietf:params:oauth:grant-type:token-exchange である必要があります。

subject_token

必須。リクエストが行われているパーティのアイデンティティを表すセキュリティトークン。

subject_token_type

必須。このパラメータは、subject_token パラメータで渡されるトークンのタイプです。標準トークン交換が使用されている場合、Keycloak は標準トークン交換で他のタイプをサポートしていないため、これは urn:ietf:params:oauth:token-type:access_token である必要があります。

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 にすることもできます。この場合、レスポンス内にアクセストークンとリフレッシュトークンの両方が含まれます。ただし、リフレッシュトークンは、標準トークン交換セクションで指定されているように、標準トークン交換でリフレッシュトークンを許可するクライアント構成オプションが有効になっている場合に許可されます。

scope

オプション。このパラメータは、クライアントがリクエストしている OAuth および OpenID Connect スコープのスペース区切りセットを表します。requester-clientオプションのクライアントスコープを使用できます。詳細については、スコープとオーディエンスを参照してください。このパラメータを省略すると、デフォルトのクライアントスコープのみが効果的に使用されることを意味します。

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 トークンが含まれている可能性があることに注意してください。この動作は、トークン交換仕様に基づいています。

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 です。

スコープとオーディエンス

トークン交換リクエストの scope パラメータは、他のグラントと同じ意味を持ちます。このパラメータはオプションです。省略した場合、リクエストで使用される有効なクライアントスコープは、requester-clientデフォルトのクライアントスコープです。このパラメータを使用する場合、有効なクライアントスコープは、デフォルトのスコープとオプションのクライアントスコープを組み合わせたものです。

デフォルトでは、使用されるクライアントスコープは、オーディエンスドキュメントで指定されているように、使用されるクライアントスコープとクライアントロールに基づいて、トークンの aud クレームにオーディエンスを追加します。

audience パラメータは、オーディエンスのフィルタリングに使用できます。これにより、aud クレームには audience パラメータで指定されたオーディエンスのみが含まれます。同様に、トークン内のクライアントロールもフィルタリングされ、トークンには audience パラメータで指定されたクライアントのクライアントロールのみが含まれます。

さらに、audience パラメータを使用して、クライアントスコープもフィルタリングできる可能性があります。ユーザーのクライアントスコープ権限と同様の方法で機能します。クライアントスコープにクライアントロールが含まれていない場合 (たとえば、ロールがゼロであるか、レルムロールのみが含まれている場合)、クライアントスコープの追加フィルタリングは発生しません。ただし、クライアントスコープにクライアントロールマッピングが含まれている場合は、audience パラメータでリクエストされたクライアントのクライアントロールをいくつか含める必要があります。コンポジットロールも考慮の対象に含まれます。クライアントスコープに audience でリクエストされたクライアントのクライアントロールが含まれていない場合、クライアントスコープはフィルタリングされます。

audience パラメータは、使用されるクライアントスコープから取得されるオーディエンスをフィルタリングするために使用できます。ただし、このパラメータはオーディエンスを追加しません。オーディエンスパラメータを省略すると、フィルタリングは発生しません。結果として、audience パラメータは、トークンを「ダウンスコーピング」して、リクエストされたオーディエンスのみが含まれるようにするために効果的に使用されます。ただし、scope パラメータはオプションのクライアントスコープを追加するために使用されるため、「アップスコーピング」してスコープを追加するために使用できます。

スコープとオーディエンスの動作をよりよく理解するために、いくつかの例を次に示します。

次のレルムがあると仮定します。

  • クライアントロール 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 のロールスコープマッピングがあります。

  • クライアントスコープ default-scope1 がデフォルトのクライアントスコープとして追加され、スコープ optional-scope2 がオプションのクライアントスコープとして追加されたクライアント requester-client

  • target-client1-roletarget-client2-role の両方のメンバーである認証済みユーザー

上記の構成は、スコープ default-scope1 を使用すると、オーディエンス target-client1 がトークンに追加され、optional-scope2 を使用すると、オーディエンス target-client2 が追加されることを意味します。これは、オーディエンスドキュメントで説明されているオーディエンス解決のためです。

例 1

scope=optional-scope2 でオーディエンスパラメータなしで送信されたトークン交換リクエスト

オーディエンスのフィルタリングはありません。スコープとオーディエンスは、クライアントスコープおよびオーディエンスドキュメントセクションで説明されているように、他のグラントの場合と同様に解決されます。レスポンス トークンは、これと似ています (この例で興味深いクレームは簡潔にするために省略されています)。

{
  "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" ]
	}
  },
  ...
}
例 2

scope=optional-scope2 および audience=target-client2 で送信されたトークン交換リクエスト

前の例と同様ですが、オーディエンスパラメータが含まれていたため、target-client1 オーディエンスとクライアントロールがフィルタリングされましたが、この target-client2 クライアントのみです。クライアントスコープ default-scope1 は、クライアントロールが含まれていますが、同時に、リクエストされたオーディエンスクライアント target-client2 のクライアントロールが含まれていないため、フィルタリングされます。したがって、トークンは次のようになります。

{
  "azp": "requester-client",
  "scope": "optional-scope2",
  "aud": [ "target-client2" ],
  "resource_access": {
    "target-client2": {
      "roles": [ "target-client2-role" ]
    }
  },
  ...
}
例 3

scope=optional-scope2 および audience=target-client2&audience=target-client3 で送信されたトークン交換リクエスト

target-client3 は、ユーザーがロールを持っていないため、トークンオーディエンスの一部ではありません。したがって、この場合、リクエストされたオーディエンスの一部が利用できないため、リクエストは拒否されます。

トークン交換仕様で述べられているように、トークンを可能な限りダウンスコーピングし、必要なオーディエンスのみを使用することをお勧めします。理想的には、単一のオーディエンスを使用します。この戦略により、リクエストが許可される可能性が高まります。
多数のさまざまなスコープとオーディエンスを含むより複雑なデプロイメントがある場合は、適切な方法でモデル化することが難しい場合があります。クライアントスコープ評価タブを使用して、特定のユーザーと、特定のスコープとオーディエンスのセットに対してトークンが期待どおりに見えるかどうかをテストすることを検討してください。

トークン交換 - 追加の詳細

これらの追加ポイントは、トークン交換の動作を明確にしています。

  • パブリッククライアントがトークン交換リクエストを送信することはサポートされていません。V1 には、パブリッククライアントがスコープを狭めてトークンを自身と交換できる場合、パブリッククライアントに対する非常に限定的なサポートがありました。このユースケースは、リフレッシュトークングラントで置き換えることができます。

  • トークン交換エンドポイントに送信される subject_token は、aud クレームにリクエスタクライアントがオーディエンスとして設定されている必要があります。そうでない場合、リクエストは拒否されます。唯一の例外は、クライアントが自身に発行された自身のトークンを交換する場合です。自身への交換は、トークンのダウンスコーピング/アップスコーピングや、不要なトークンオーディエンスのフィルタリングなどに役立つ場合があります。

  • 同意 - リクエスタクライアントで 同意が必要 が有効になっている場合、トークン交換は、ユーザーがリクエストされたすべてのスコープに対してすでに同意を付与されている場合にのみ許可されます。

  • きめ細かい管理者権限 (FGAP) は、標準トークン交換には必要ありません。将来的には FGAP と統合する予定ですが、その統合はすべてのグラントで利用できるようになる可能性があります。トークン交換 V1 のように、トークン交換に固有のものではありません。

  • トークン交換とクライアントポリシーの統合が可能です。この統合は、特定のユースケースに対処するのに役立ちます。たとえば、クライアント requester-clientscope=some-confidential-scope でリクエストを送信した場合にトークン交換リクエストを拒否するユースケースを考えてみましょう。この例では、client-scopegrant-typeclient-roles の複合条件を持つクライアントポリシー条件を作成すると役立つ場合があります。

  • リフレッシュトークンのリクエストは、クライアントに 標準トークン交換でリフレッシュトークンを許可する スイッチが No (デフォルト値) 以外の値に設定されている場合にのみ許可されます。スイッチは、OpenID Connect 互換モードセクションの OIDC クライアントの 詳細設定 タブの管理コンソールで使用できます。スイッチの他の利用可能な値は 同じセッション です。これは、リフレッシュトークンがサブジェクトトークンと同じユーザーセッションを使用できる場合にのみリフレッシュトークンが許可されることを意味します。そのサブジェクトトークンが一時セッションまたはオフラインセッションからのものである場合、リフレッシュトークンのリクエストは許可されません。同様に、オフライン トークン (scope=offline_access を使用) をリクエストすることも許可されません。

Enabling refresh token in Token Exchange
図 2. トークン交換でのリフレッシュトークンの有効化
  • トークン交換は、新しいユーザーセッションを生成することはありません。requested_token_type がリフレッシュトークンの場合、リクエスタクライアントのユーザーセッションに新しいクライアントセッションが最終的に作成される可能性があります (クライアントセッションがまだ作成されていない場合)。

  • Keycloak トークン交換は、resource パラメータをまだサポートしていません。

  • トークン交換仕様では、偽装と委任の概念について言及しています。Keycloak は偽装ユースケースをサポートしていますが、委任ユースケースはまだサポートしていません。

失効

クライアント initial-client に発行されたサブジェクトトークン access-token1 があると仮定すると、トークン失効に関連する考慮事項を次に示します。

  • access-token1 がクライアント requester-clientaccess-token2 に交換された場合、access-token1 の失効は access-token2 を失効させません。アクセストークンの「失効チェーン」のサポートは、かなりのオーバーヘッドを意味します。したがって、これを考慮して、管理者はアクセストークンが短寿命であり、一定時間後に自動的に失効することを確認する必要があります。

  • access-token1 がクライアント requester-clientrefresh-token2 に交換された場合、失効チェーンのサポートを試みます。これは、次のことを意味します。

    • access-token1 の失効は、refresh-token2 も失効させます。さらに、これにより、ユーザーセッションからクライアント requester-client のクライアントセッションが削除されるため、このユーザーセッションの requester-client のすべてのリフレッシュトークンが効果的に失効します。

    • refresh-token2 とそれに関連するアクセストークンが、別のクライアントへのさらなるトークン交換に使用された場合、access-token1 の失効は、それらの後続のトークン交換も失効させます。言い換えれば、交換されたトークンの「チェーン」全体が失効することになります。

    • 失効エンドポイントが呼び出されたときにアクセストークンが有効である必要があることに注意してください。元の access-token1 の有効期限が切れたときに有効なアクセストークンがない場合は、同じユーザーセッションで同じクライアントに発行された別のアクセストークンを使用できる可能性があります。refresh-token2 などの交換されたトークンと「チェーン」からの他のトークンは失効する必要があります。

標準トークン交換とレガシートークン交換の比較

前のセクションでは、標準トークン交換とレガシートークン交換について詳しく説明しましたが、以下は、2 つのトークン交換方法を比較する全体的な概要です。

機能 標準トークン交換 V2 レガシートークン交換 V1

内部 - 内部トークン交換

サポートされています。rfc8693 に従って実装されています。

プレビューサポート。rfc8693 の緩い実装。代わりに V2 を使用することをお勧めします。

許可されている subject_token_type

アクセストークンタイプのみ

内部 - 内部の場合はアクセストークンタイプのみ、外部 - 内部シナリオの場合は JWT

許可されている requested_token_type

アクセストークン (デフォルト)、リフレッシュトークン、ID トークン

アクセストークン、リフレッシュトークン (デフォルト)、SAML2 アサーション

scope パラメータの動作

他のグラントに準拠。スコープパラメータは、トークン交換リクエストを送信したクライアントのオプションのスコープをリクエストすることを意味します。

オーディエンスパラメータで指定された「ターゲット」クライアントのスコープに基づくスコープパラメータ。ダウンスコーピングのみをサポート

audience パラメータの動作

仕様に従ってより多くの値をサポート。利用可能なオーディエンスを絞り込み、リクエストされたオーディエンスのみを保持するために使用できます。事実上、必要なターゲットオーディエンスごとにトークンをダウンスコーピングします。

単一のオーディエンス値をサポート。事実上、オーディエンスパラメータでリクエストされたクライアントに発行され、そのクライアントのスコープを使用するトークン

パブリッククライアント

利用できません。V1 で実装されたダウンスコーピングは、リフレッシュトークングラントで置き換えることができます。

クライアント自体のトークンを交換する場合にのみ利用可能。事実上、ダウンスコーピングのみをサポート

同意

ユーザーがすでに同意を付与されている限り、同意が必要なクライアントで許可されます。

同意が必要なクライアントでは許可されていません。

承認

リクエスタクライアントが subject_token のオーディエンス内にある必要があることの検証。クライアントポリシーとの統合。きめ細かい管理者権限はありません。

きめ細かい管理者権限バージョン 1 に基づいています。

失効チェーン

アクセストークンでは利用できません。リフレッシュトークンで利用可能

アクセストークンとリフレッシュトークンの両方で利用できません。

rfc8693 に基づく委任

まだサポートされていません

サポートされていません

rfc8693 に基づくリソースパラメータ

まだサポートされていません

サポートされていません

フェデレーショントークン交換

まだ実装されていません

プレビューとして実装

サブジェクト偽装 (直接的なネイキッド偽装を含む)

まだ実装されていません

プレビューとして実装

レガシートークン交換

トークン交換は プレビュー であり、完全にはサポートされていません。この機能はデフォルトで無効になっています。

有効にするには、--features=preview または --features=token-exchange でサーバーを起動します。

トークン交換は テクノロジープレビュー であり、完全にはサポートされていません。

内部トークンから内部トークンへの交換フロー以上のものを使用するには、admin-fine-grained-authz 機能も有効にします。詳細については、機能の有効化と無効化ガイドを参照してください。

トークン交換の仕組み

Keycloak では、トークン交換は、一連のクレデンシャルまたはトークンを使用して、まったく異なるトークンを取得するプロセスです。クライアントは、信頼性の低いアプリケーションで呼び出したい場合があるため、現在持っているトークンをダウングレードしたい場合があります。クライアントは、Keycloak トークンを、リンクされたソーシャルプロバイダーアカウント用に保存されたトークンと交換したい場合があります。他の Keycloak レルムまたは外部 IDP によって発行された外部トークンを信頼したい場合があります。クライアントは、ユーザーを偽装する必要がある場合があります。Keycloak の現在のトークン交換に関する機能の簡単な概要を次に示します。

  • クライアントは、特定のクライアント用に作成された既存の Keycloak トークンを、別のクライアントを対象とした新しいトークンと交換できます。

  • クライアントは、既存の Keycloak トークンを、外部トークン (つまり、リンクされた Facebook アカウント) と交換できます。

  • クライアントは、外部トークンを Keycloak トークンと交換できます。

  • クライアントは、ユーザーを偽装できます。

Keycloak のトークン交換は、IETF の OAuth トークン交換仕様の非常に緩い実装です。少し拡張したり、一部を無視したり、仕様の他の部分を緩く解釈したりしました。これは、レルムの OpenID Connect トークンエンドポイントでの単純なグラントタイプ呼び出しです。

/realms/{realm-name}/protocol/openid-connect/token

フォームパラメータ (application/x-www-form-urlencoded) を入力として受け入れ、出力は交換をリクエストしたトークンのタイプによって異なります。トークン交換はクライアントエンドポイントであるため、リクエストは呼び出しクライアントの認証情報を提供する必要があります。パブリッククライアントは、フォームパラメータとしてクライアント識別子を指定します。コンフィデンシャルクライアントは、フォームパラメータを使用してクライアント ID とシークレット、Basic Auth、または管理者がレルムでクライアント認証フローを構成した方法を渡すこともできます。

フォームパラメータ

client_id

必須の場合あり。このパラメータは、フォームパラメータを認証に使用するクライアントに必須です。Basic Auth、クライアント JWT トークン、またはクライアント証明書認証を使用している場合は、このパラメータを指定しないでください。

client_secret

必須の場合あり。このパラメータは、フォームパラメータを認証に使用し、クライアントシークレットをクレデンシャルとして使用するクライアントに必須です。レルムでのクライアント呼び出しが別の手段で認証されている場合は、このパラメータを指定しないでください。

grant_type

必須。パラメータの値は urn:ietf:params:oauth:grant-type:token-exchange である必要があります。

subject_token

オプション。リクエストが行われているパーティのアイデンティティを表すセキュリティトークン。既存のトークンを新しいトークンと交換する場合は必須です。

subject_issuer

オプション。subject_token の発行者を識別します。トークンが現在のレルムからのものである場合、または発行者を subject_token_type から特定できる場合は、空白のままにすることができます。それ以外の場合は、指定する必要があります。有効な値は、レルム用に構成された ID プロバイダー のエイリアスです。または、特定の ID プロバイダー によって構成された発行者クレーム識別子。

subject_token_type

オプション。このパラメータは、subject_token パラメータで渡されるトークンのタイプです。subject_token がレルムからのものであり、アクセストークンである場合、デフォルトは urn:ietf:params:oauth:token-type:access_token です。外部トークンの場合、このパラメータは、subject_issuer の要件に応じて、指定する必要がある場合とない場合があります。

requested_token_type

オプション。このパラメータは、クライアントが交換したいトークンのタイプを表します。現在、OAuth および OpenID Connect トークンタイプのみがサポートされています。このデフォルト値は、urn:ietf:params:oauth:token-type:refresh_token であるかどうかによって異なります。その場合、レスポンス内にアクセストークンとリフレッシュトークンの両方が返されます。その他の適切な値は、urn:ietf:params:oauth:token-type:access_token および urn:ietf:params:oauth:token-type:id_token です。

audience

オプション。このパラメータは、新しいトークンをミントしたいターゲットクライアントを指定します。

requested_issuer

オプション。このパラメータは、クライアントが外部プロバイダーによってミントされたトークンを要求することを指定します。レルム内で構成された ID プロバイダー のエイリアスである必要があります。

requested_subject

オプション。クライアントが別のユーザーを偽装したい場合に、ユーザー名またはユーザー ID を指定します。

scope

オプション。このパラメータは、クライアントがリクエストしている OAuth および OpenID Connect スコープのターゲットセットを表します。返されるスコープは、スコープパラメータとアクセストークンスコープのデカルト積です。

現在、OpenID Connect および OAuth 交換のみをサポートしています。SAML ベースのクライアントと ID プロバイダーのサポートは、ユーザーの需要に応じて将来追加される可能性があります。

トークン交換リクエストからのレスポンス

交換呼び出しからの成功したレスポンスは、クライアントが要求する requested-token-type および requested_issuer に応じたコンテンツタイプで HTTP 200 レスポンスコードを返します。OAuth リクエストされたトークンタイプは、OAuth トークン交換仕様で説明されている JSON ドキュメントを返します。

{
   "access_token" : ".....",
   "refresh_token" : ".....",
   "expires_in" : "...."
 }

リフレッシュトークンをリクエストするクライアントは、レスポンスでアクセストークンとリフレッシュトークンの両方を取得します。アクセストークンタイプのみをリクエストするクライアントは、レスポンスでアクセストークンのみを取得します。requested_issuer パラメータを介して外部発行者をリクエストするクライアントの場合、有効期限情報が含まれる場合と含まれない場合があります。

エラーレスポンスは一般的に400 HTTPレスポンスコードのカテゴリに分類されますが、エラーの重大度によっては、他のエラーレスポンスコードが返されることもあります。エラーレスポンスには、requested_issuer に応じてコンテンツが含まれる場合があります。OAuthベースの交換では、次のようなJSONドキュメントが返されることがあります。

{
   "error" : "...."
   "error_description" : "...."
}

交換タイプによっては、追加のエラークレームが返される場合があります。例えば、OAuthアイデンティティプロバイダーは、ユーザーがアイデンティティプロバイダーへのリンクを持っていない場合、追加の account-link-url クレームを含めることがあります。このリンクは、クライアント主導のリンク要求に使用できます。

トークン交換の設定には、きめ細かい管理者権限の知識が必要です(詳細については、サーバー管理ガイドを参照してください)。交換を行うには、クライアントに許可を付与する必要があります。これについては、この章の後半で詳しく説明します。

この章の残りの部分では、設定要件について説明し、さまざまな交換シナリオの例を示します。簡略化のために、現在のレルムによって発行されたトークンを内部トークン、外部レルムまたはアイデンティティプロバイダーによって発行されたトークンを外部トークンと呼びましょう。

内部トークンから内部トークンへの交換

内部トークンから内部トークンへの交換には、以下で説明するレガシートークン交換フローを使用する代わりに、標準トークン交換を使用することをお勧めします。標準トークン交換は公式にサポートされています。

内部トークンからトークンへの交換では、特定のクライアントに発行された既存のトークンがあり、このトークンを別のターゲットクライアント用に発行された新しいトークンと交換したいと考えています。なぜこれを行う必要があるのでしょうか?これは一般的に、クライアントが自身に発行されたトークンを持っており、アクセストークン内で異なるクレームと権限を必要とする他のアプリケーションに追加のリクエストを行う必要がある場合に発生します。このタイプの交換が必要になる可能性のある他の理由としては、アプリが信頼性の低いアプリを呼び出す必要があり、現在のアクセストークンを伝播したくない場合に「権限のダウングレード」を実行する必要がある場合などがあります。

クライアント間交換の許可

異なるクライアントのトークンを交換したいクライアントは、管理コンソールで承認される必要があります。交換を許可したいターゲットクライアントで、token-exchange のきめ細かい権限を定義する必要があります。

Target Client Permission
図3. ターゲットクライアントの権限
手順
  1. 権限有効オンに切り替えます。

    Target Client Exchange Permission Set
    図4. ターゲットクライアントの権限

    そのページには、token-exchange リンクが表示されます。

  2. そのリンクをクリックして、権限の定義を開始します。

    この設定ページが表示されます。

    Target Client Exchange Permission Setup
    図5. ターゲットクライアント交換権限の設定
  3. 画面上部のブレッドクラムにあるクライアントの詳細をクリックします。

  4. この権限のポリシーを定義します。

  5. 画面上部のブレッドクラムにある認可をクリックします。

  6. この権限のポリシーを定義します。

  7. ポリシータブをクリックします。

  8. ポリシーを作成ボタンをクリックして、クライアントポリシーを作成します。

    Client Policy Creation
    図6. クライアントポリシーの作成
  9. トークン交換を要求している認証済みクライアントである開始クライアントを入力します。

  10. このポリシーを作成したら、ターゲットクライアントのtoken-exchange権限に戻り、先ほど定義したクライアントポリシーを追加します。

    Apply Client Policy
    図7. クライアントポリシーの適用

これで、クライアントは呼び出しを行う権限を持ちました。これを正しく行わないと、交換を試みた場合に403 Forbiddenレスポンスが返されます。

リクエストの作成

クライアントが既存のトークンを別のクライアントをターゲットとするトークンと交換する場合、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" \
    https://127.0.0.1:8080/realms/myrealm/protocol/openid-connect/token

subject_token パラメータは、ターゲットレルムのアクセストークンである必要があります。requested_token_type パラメータがリフレッシュトークンタイプの場合、レスポンスにはアクセストークン、リフレッシュトークン、および有効期限が含まれます。以下は、この呼び出しから返されるJSONレスポンスの例です。

audience パラメータが設定されていない場合、パラメータの値はトークン交換リクエストを行うクライアントにデフォルト設定されます。

機密クライアントとは異なり、パブリッククライアントは他のクライアントからのトークンを使用してトークン交換を実行することは許可されていません。subject_token を渡す場合、トークンが発行された(機密)クライアントは、リクエストを行うクライアントと一致するか、または異なるクライアントに発行された場合は、リクエストを行うクライアントがトークンに設定されたオーディエンスに含まれている必要があります。

ターゲット audience (リクエストを行うクライアントとは異なるクライアント)を明示的に設定する場合は、リクエストを行うクライアントが交換を正常に完了できるように、audience パラメータに設定されたクライアントに対して token-exchange スコープ権限が設定されていることも確認する必要があります。

{
   "access_token" : "....",
   "refresh_token" : "....",
   "expires_in" : 3600
}

内部トークンから外部トークンへの交換

レルムトークンを、外部アイデンティティプロバイダーによって発行された外部トークンと交換できます。この外部アイデンティティプロバイダーは、管理コンソールの Identity Provider セクション内で設定されている必要があります。現在、OAuth/OpenID Connectベースの外部アイデンティティプロバイダーのみがサポートされており、これにはすべてのソーシャルプロバイダーが含まれます。Keycloakは、外部プロバイダーへのバックチャネル交換を実行しません。したがって、アカウントがリンクされていない場合、外部トークンを取得することはできません。外部トークンを取得できるようにするには、次のいずれかの条件を満たす必要があります。

  • ユーザーが少なくとも一度は外部アイデンティティプロバイダーでログインしていること

  • ユーザーがユーザーアカウントサービスを通じて外部アイデンティティプロバイダーとリンクされていること

  • ユーザーアカウントが、クライアント主導のアカウントリンク APIを使用して外部アイデンティティプロバイダーを通じてリンクされていること。

最後に、外部アイデンティティプロバイダーがトークンを保存するように設定されているか、または上記のいずれかのアクションが、交換する内部トークンと同じユーザーセッションで実行されている必要があります。

アカウントがリンクされていない場合、交換レスポンスには、アカウントを確立するために使用できるリンクが含まれます。これについては、リクエストの作成セクションで詳しく説明します。

外部交換の許可

内部トークンから外部トークンへの交換リクエストは、呼び出し元クライアントが外部アイデンティティプロバイダーとトークンを交換する許可を付与するまで、403 Forbiddenレスポンスで拒否されます。クライアントに許可を付与するには、アイデンティティプロバイダーの設定ページのPermissionsタブに移動します。

Identity Provider Exchange Permission
図8. アイデンティティプロバイダーの権限
手順
  1. 権限有効オンに切り替えます。

    Identity Provider Exchange Permission Set
    図9. アイデンティティプロバイダーの権限

    ページには、token-exchangeリンクが表示されます。

  2. リンクをクリックして、権限の定義を開始します。

    この設定ページが表示されます。

    Identity Provider Exchange Permission Setup
    図10. アイデンティティプロバイダー交換権限の設定
  3. 画面上部のブレッドクラムにあるクライアントの詳細をクリックします。

  4. ポリシータブをクリックして、クライアントポリシーを作成します。

    Client Policy Creation
    図11. クライアントポリシーの作成
  5. トークン交換を要求している認証済みクライアントである開始クライアントを入力します。

  6. アイデンティティプロバイダーのtoken-exchange権限に戻り、先ほど定義したクライアントポリシーを追加します。

    Apply Client Policy
    図12. クライアントポリシーの適用

これで、クライアントは呼び出しを行う権限を持ちました。これを正しく行わないと、交換を試みた場合に403 Forbiddenレスポンスが返されます。

リクエストの作成

クライアントが既存の内部トークンを外部トークンと交換する場合、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" \
    https://127.0.0.1:8080/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_expired または not_linked のいずれかになります。account-link-url クレームは、クライアントが クライアント主導のアカウントリンク を実行できるように提供されています。ほとんど、すべてではありませんが、プロバイダーはブラウザOAuthプロトコルによるリンクを必要とします。account-link-urlredirect_uri クエリパラメータを追加するだけで、ブラウザを転送してリンクを実行できます。

外部トークンから内部トークンへの交換

外部アイデンティティプロバイダーによって発行された外部トークンを信頼し、内部トークンと交換できます。これは、レルム間をブリッジしたり、ソーシャルプロバイダーからのトークンを信頼したりするために使用できます。新しいユーザーが存在しない場合、レルムにインポートされるという点で、アイデンティティプロバイダーのブラウザログインと同様に機能します。

外部トークン交換の現在の制限は、外部トークンが既存のユーザーにマッピングされる場合、既存のユーザーがすでに外部アイデンティティプロバイダーへのアカウントリンクを持っている場合を除き、交換が許可されないことです。

交換が完了すると、レルム内にユーザーセッションが作成され、requested_token_type パラメータ値に応じて、アクセスおよび/またはリフレッシュトークンを受け取ります。この新しいユーザーセッションは、タイムアウトするか、この新しいアクセストークンを渡してレルムのログアウトエンドポイントを呼び出すまでアクティブなままになることに注意してください。

これらのタイプの変更には、管理コンソールで設定されたアイデンティティプロバイダーが必要です。

SAMLアイデンティティプロバイダーは現時点ではサポートされていません。Twitterトークンも交換できません。

交換の許可

外部トークン交換を実行する前に、呼び出し元クライアントに交換を行う許可を付与します。この許可は、内部から外部への許可が付与されるのと同じ方法で付与されます。

audience パラメータも指定し、その値が呼び出し元クライアントとは異なるクライアントを指している場合は、audience パラメータで指定されたターゲットクライアントへの交換を呼び出し元クライアントに許可する必要があります。これを行う方法は、このセクションの前述で説明されています。

リクエストの作成

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内の iss (発行者)クレームを介して照合されます。これは、プロバイダーのエイリアス、またはプロバイダー構成内で登録された発行者である必要があります。

検証のため、トークンがアクセストークンの場合、プロバイダーのユーザー情報サービスが呼び出されてトークンが検証されます。呼び出しが成功すると、アクセストークンが有効であることを意味します。サブジェクトトークンがJWTであり、プロバイダーが署名検証を有効にしている場合、それが試行されます。それ以外の場合は、ユーザー情報サービスを呼び出してトークンを検証することもデフォルトになります。

デフォルトでは、発行される内部トークンは、呼び出し元クライアントを使用して、呼び出し元クライアントに定義されたプロトコルマッパーを使用してトークンに何が入るかを決定します。または、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" \
    https://127.0.0.1:8080/realms/myrealm/protocol/openid-connect/token

requested_token_type パラメータがリフレッシュトークンタイプの場合、レスポンスにはアクセストークン、リフレッシュトークン、および有効期限が含まれます。以下は、この呼び出しから返されるJSONレスポンスの例です。

{
   "access_token" : "....",
   "refresh_token" : "....",
   "expires_in" : 3600
}

なりすまし

内部および外部トークン交換の場合、クライアントはユーザーに代わって別のユーザーになりすますように要求できます。たとえば、サポートエンジニアが問題をデバッグできるように、ユーザーになりすます必要がある管理者アプリケーションがある場合があります。

ここで言及されているなりすましシナリオは、トークン交換仕様のなりすまし概念とは異なります。仕様は、トークンサブジェクトを異なるサブジェクトになりすますことをサポートしていません。仕様のセマンティクスは、むしろ「ユーザーになりすます」のではなく、「クライアントになりすます」ことを意味します。

交換の許可

サブジェクトトークンが表すユーザーは、他のユーザーになりすます権限を持っている必要があります。この権限を有効にする方法については、サーバー管理ガイドを参照してください。これは、ロールまたはきめ細かい管理者権限を通じて行うことができます。

リクエストの作成

他の章で説明されているようにリクエストを作成しますが、さらに 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" \
    https://127.0.0.1:8080/realms/myrealm/protocol/openid-connect/token

直接的なネイキッドなりすまし

subject_token を指定せずに、内部トークン交換リクエストを作成できます。これは、クライアントがレルム内の任意のユーザーになりすますことができるため、クライアントに多くの信頼を置くため、直接的なネイキッドなりすましと呼ばれます。交換するサブジェクトトークンを取得することが不可能なアプリケーションをブリッジするために、これが必要になる場合があります。たとえば、LDAPで直接ログインを実行するレガシーアプリケーションを統合している場合があります。その場合、レガシーアプリはユーザー自身を認証できますが、トークンを取得することはできません。

クライアントに対して直接的なネイキッドなりすましを有効にすることは非常に危険です。クライアントの認証情報が盗まれた場合、そのクライアントはシステム内の任意のユーザーになりすますことができます。

交換の許可

audience パラメータが指定されている場合、呼び出し元クライアントはクライアントへの交換を許可されている必要があります。これを設定する方法については、この章の前述で説明しています。

さらに、呼び出し元クライアントはユーザーになりすます権限を付与されている必要があります。

手順
  1. メニューのユーザーをクリックします。

  2. 権限タブをクリックします。

    User Permissions
    図13. ユーザー権限
  3. 権限有効オンに切り替えます。

    Users Impersonation Permission Set
    図14. アイデンティティプロバイダーの権限

    ページには、なりすましリンクが表示されます。

  4. そのリンクをクリックして、権限の定義を開始します。

    この設定ページが表示されます。

    Users Impersonation Permission Setup
    図15. ユーザーなりすまし権限の設定
  5. 画面上部のブレッドクラムにあるクライアントの詳細をクリックします。

  6. この権限のポリシーを定義します。

  7. ポリシータブに移動し、クライアントポリシーを作成します。

    Client Policy Creation
    図16. クライアントポリシーの作成
  8. トークン交換を要求している認証済みクライアントである開始クライアントを入力します。

  9. ユーザーのなりすまし権限に戻り、先ほど定義したクライアントポリシーを追加します。

    Apply Client Policy
    図17. クライアントポリシーの適用

これで、クライアントはユーザーになりすます権限を持ちました。これを正しく行わないと、このタイプの交換を試みた場合に403 Forbiddenレスポンスが返されます。

パブリッククライアントは、直接的なネイキッドなりすましを行うことは許可されていません。

リクエストの作成

リクエストを作成するには、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" \
    https://127.0.0.1:8080/realms/myrealm/protocol/openid-connect/token

サービスアカウントで権限モデルを拡張する

クライアントに交換の許可を付与する場合、必ずしもすべてのクライアントに対して手動でこれらの権限を有効にする必要はありません。クライアントにサービスアカウントが関連付けられている場合、ロールを使用して権限をグループ化し、クライアントのサービスアカウントにロールを割り当てることで交換権限を割り当てることができます。たとえば、naked-exchange ロールを定義すると、そのロールを持つサービスアカウントはネイキッド交換を実行できます。

交換の脆弱性

トークン交換を許可し始めると、注意すべき点と注意すべき点がいくつかあります。

最初の点はパブリッククライアントです。パブリッククライアントは、交換を実行するためにクライアント認証情報を必要としません。有効なトークンを持っている人は誰でも、パブリッククライアントをなりすまし、そのパブリッククライアントが許可されている交換を実行できます。レルムによって管理されている信頼できないクライアントがある場合、パブリッククライアントは権限モデルに脆弱性をもたらす可能性があります。これが、直接的なネイキッド交換がパブリッククライアントを許可せず、呼び出し元クライアントがパブリッククライアントの場合にエラーで中止する理由です。

Facebook、Googleなどによって提供されるソーシャルトークンをレルムトークンと交換することができます。交換トークンが何を行うことを許可されているかに注意し、警戒してください。これらのソーシャルウェブサイトで偽のアカウントを作成することは難しくありません。デフォルトのロール、グループ、およびアイデンティティプロバイダーマッパーを使用して、外部ソーシャルユーザーに割り当てられる属性とロールを制御します。

直接的なネイキッド交換は非常に危険です。呼び出し元クライアントがクライアント認証情報を漏洩させないことに多くの信頼を置いています。これらの認証情報が漏洩した場合、泥棒はシステム内の誰にでもなりすますことができます。これは、既存のトークンを持つ機密クライアントとは対照的です。アクセストークンとクライアント認証情報の2つの認証要素があり、1人のユーザーのみを扱っています。したがって、直接的なネイキッド交換は控えめに使用してください。

このページについて