RFC 8693 - OAuth 2.0 Token Exchange メモ


* セキュリティトークンサービスにおけるセキュリティトークン交換リクエスト仕様に関するメモ

* 次のようなAPI Gateway経由でバックエンドのAPIを呼び出す場合、クライアントがAPI Gatewayにアクセスする際に使用するトークンを再利用せず、API Gateway - API通信用のトークンを発行する際に利用する。

  ![token_exchange.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586535/ebba3581-7871-2d97-a9ea-cf28b0265c56.png)


  * セキュリティトークン
    * 異種環境またはセキュリティドメイン間でのIDおよびセキュリティ情報の共有を目的とした情報
    * 例:JSON Web Tokens（JWT）、Security Assertion Markup Language（SAML）2.0アサーション
  * セキュリティトークンサービス（STS）
    * 提供されたセキュリティトークンを検証し、それに応じて新しいセキュリティトークンを発行するサービス

## Token Exchange Request and Response

* 拡張グラントタイプを指定してトークンエンドポイントにリクエストを行うことでセキュリティトークンを取得する。
* リクエスト例

```http
POST /as/token.oauth2 HTTP/1.1
Host: as.example.com
Authorization: Basic cnMwODpsb25nLXNlY3VyZS1yYW5kb20tc2VjcmV0
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange &resource=https%3A%2F%2Fbackend.example.com%2Fapi
&subject_token=accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC
&subject_token_type= urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token
```

* `grant_type` : 必須。`urn:ietf:params:oauth:grant-type:token-exchange`は、トークン交換を意味する。
* `resource` : 任意。クライアントがセキュリティトークンを使用するターゲットサービスまたはリソースを示すURI。
* `requested_token_type` : セキュリティトークンの識別子。
* `subject_token` : 必須。要求元の認可を示すセキュリティトークン。アクセストークンなど。
* `subject_token_type` : 必須。`subject_token`パラメータのセキュリティトークンのタイプを示す識別子。

※その他にも任意パラメータあり。

* 成功した場合、200応答が返却される。

* レスポンス例

  ```http
  HTTP/1.1 200 OK
  Content-Type: application/json
  Cache-Control: no-cache, no-store
   
  {
  　"access_token":"eyJhbGciOiJFUzI1NiIsImtpZCI6IjllciJ9.eyJhdWQiOiJodHRwczovL2JhY2tlbmQuZXhhbXBsZS5jb20iLCJpc3MiOiJodHRwczovL2FzLmV4YW1wbGUuY29tIiwiZXhwIjoxNDQxOTE3NTkzLCJpYXQiOjE0NDE5MTc1MzMsInN1YiI6ImJkY0BleGFtcGxlLmNvbSIsInNjb3BlIjoiYXBpIn0.40y3ZgQedw6rxf59WlwHDD9jryFOr0_Wh3CGozQBihNBhnXEQgU85AI9x3KmsPottVMLPIWvmDCMy5-kdXjwhw",
  　"issued_token_type":"urn:ietf:params:oauth:token-type:access_token",
  　"token_type":"Bearer",
  　"expires_in":60
  }
  ```

* `access_token` : トークン交換リクエストで発行されたアクセストークン。

* `issued_token_type` : トークン種別。

* `token_type` : 発行されたアクセストークンの使用方法を指定する。`Bearer`はクライアントがトークン自体の内容以外の追加証明なしに、そのまま提示できることを意味する。

* `expires_in` : 有効期間。秒単位。

## Token Type Identifiers

* トークン種別の識別子は次のものあg定義されている。
  * `urn:ietf:params:oauth:token-type:access_token` : トークンが、指定された認可サーバーによって発行されたOAuth 2.0アクセストークンであることを示す。
  * `urn:ietf:params:oauth:token-type:refresh_token`トークンが、指定された認可サーバーによって発行されたOAuth 2.0リフレッシュトークンであることを示す。
  * `urn:ietf:params:oauth:token-type:id_token` : トークンがIDトークンであることを示す。
  * `urn:ietf:params:oauth:token-type:saml1` : トークンがbase64urlでエンコードされたSAML 1.1 アサーションであることを示す。
  * `urn:ietf:params:oauth:token-type:saml2` : トークンがbase64urlでエンコードされたSAML 2.0アサーションであることを示す。
  * `urn:ietf:params:oauth:token-type:jwt` : トークンがJWTであることを示す。

## JSON Web Token Claims and Introspection Response Parameters

* トークン内で権限委任を表現する仕組みとして以下のクレームを定義している。

* `act（Actor）`クレーム

  * 権限委任が発生したことを表し、権限が委任された代理主体を識別するための手段を提供する。

  * 値はJSONオブジェクトで、JSONオブジェクトのメンバーはアクターを識別するクレームとなる。

    ```json
    {
          "aud":"https://consumer.example.com",
          "iss":"https://issuer.example.com",
          "exp":1443904177,
          "nbf":1443904077,
          "sub":"user@example.com",
          "act":
          {
            "sub":"admin@example.com"
          }
    }
    ```

  * ↑はトークン自体のクレームはuser@example.comに関するものであるが、`act`クレームによりadmin@example.comが現在のアクターであることを示している。

  * ある「act」クレームを別のクレーム内にネストすることによって権限の連鎖関係を表現できる。

    * 最も外側の`act`クレームは現在のアクターを表し、ネストされた`act`クレームは以前のアクターを表す。

    * 次の例では、トークン自体のクレームはuser@example.comに関するものだが、`act`クレームは、システム<https://service16.example.com>が現在のアクターであり、以前のアクターは<https://service77.example.com>であることを示している。

      * service16がservice77からの呼び出しでトークンを受信し、それをservice26の呼び出しに適したトークンと交換した結果として発生した。

      ```json
      {
            "aud":"https://service26.example.com",
            "iss":"https://issuer.example.com",
            "exp":1443904100,
            "nbf":1443904000,
            "sub":"user@example.com",
            "act":
            {
              "sub":"https://service16.example.com",
              "act":
              {
                "sub":"https://service77.example.com"
              }
            }
      }
      ```

* `scope (Scopes) `クレーム

  * トークンに関連付けられたスコープのスペース区切りのリストからなるJSON文字列

    ```json
        {
          "aud":"https://consumer.example.com",
          "iss":"https://issuer.example.com",
          "exp":1443904177,
          "nbf":1443904077,
          "sub":"dgaf4mvfs75Fci_FL3heQA",
          "scope":"email profile phone address"
        }
    ```

* `client_id(Client Identifier)`クレーム

  * トークンを要求したOAuth 2.0 クライアントのクライアント識別子

    ```json
    {
          "aud":"https://consumer.example.com",
          "iss":"https://issuer.example.com",
          "exp":1443904177,
          "sub":"user@example.com",
          "client_id":"s6BhdRkqt3"
    }
    ```

* `may_act(Authorized Actor)`クレーム

  * ある主体がactorとなり、別の主体に代わって処理することを許可されていることを表す。

  * 次の例では、トークン自体のクレームはuser@example.comに関するものであるが、`may_act`クレームは、admin @ example.comがuser@example.comに代わって処理することを許可されていることを示す。

    ```json
    {
          "aud":"https://consumer.example.com",
          "iss":"https://issuer.example.com",
          "exp":1443904177,
          "nbf":1443904077,
          "sub":"user@example.com",
          "may_act":
          {
            "sub":"admin@example.com"
          }
    }
    ```

    

## 参考情報

* [RFC 8693 - OAuth 2.0 Token Exchange 日本語訳](https://tex2e.github.io/rfc-translater/html/rfc8693.html)
* [Keycloak で OAuth 2.0 Token Exchange を試してみる](https://www.techscore.com/blog/2019/09/06/keycloak-oauth-2-0-token-exchange/)