GAIN POC中間報告 (2023年春)

# 免責条項

本記事は、2023年3月26日に私がGAIN PoCコミュニティーグループに提出したGAIN PoCに関する中間報告の日本語訳です。中間報告は好評とともにグループに受け入れられたものの、公式に承認された文書ではない点には注意してください。英語原文は『[GAIN PoC Interim Report (Spring 2023)](https://darutk.medium.com/gain-poc-interim-report-spring-2023-e02a1cd90b1c)』です。

# 序文

<ruby>**<rb>GAIN</rb>**<rp>(</rp><rt>ゲイン</rt><rp>)</rp></ruby> (**Global Assured Identity Network**) はインターネット上に高信頼性デジタルアイデンティティネットワークを構築するためのプロジェクトです。関連する業界の150名以上の専門家が共著者となり、ホワイトペーパー『[GAIN DIGITAL TRUST](https://gainforum.org/GAINWhitePaper.pdf)』を書き上げ、2021年9月13日に公開しました。同ホワイトペーパーは[GAINのウェブサイト](https://gainforum.org/)から無料でダウンロードできます。

GAINの技術的実現性を調査するため、ボランティアによって[GAIN PoCコミュニティーグループ](https://openid.net/gainpoc/)が作られました。この文書は、コミュニティーグループの一年の活動を通じて得た知見に関する中間報告です。

# 現行のエコシステムアーキテクチャ

英国から始まったオープンバンキングのムーブメントは世界の国々に広がりました。[オーストラリアの消費者データ権](https://www.cdr.gov.au/) (Consumer Data Right) や[ブラジルのオープンファイナンス](https://openfinancebrasil.org.br/)はそのような事例です。

各国はそれぞれ、サービス群がアプリケーション群とやりとりするためのエコシステムを構築しました。技術的には、サービスとアプリケーションは『認可サーバー』と『リライングパーティー』を実装しました。それらの技術的な詳細は[RFC 6749 The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749.html)および関連仕様で定義されています。

典型的な実装では、リライングパーティーは一度に一つの認可サーバーに属します。そのような関係を持たない認可サーバーとリライングパーティーはやりとりすることができません。そのため、アプリケーションが複数のサービスとやりとりしたい場合、アプリケーションはそれぞれのサービスの認可サーバーと一つ一つ関係を築かなければなりません。

![current_ecosystem.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/88025867-c578-75ce-8b01-552498e2e705.png)

関係を築くため、アプリケーションは『動的クライアント登録』と呼ばれる仕組みを使い、自身を各認可サーバーに登録します。その仕組みは[OpenID Connect Dynamic Client Registration 1.0](https://openid.net/specs/openid-connect-registration-1_0.html)や[RFC 7591 OAuth 2.0 Dynamic Client Registration Protocal](https://www.rfc-editor.org/rfc/rfc7591.html)で定義されています。

動的クライアント登録をサポートする認可サーバーは動的クライアント登録エンドポイントを提供します。このエンドポイントは、クライアントメタデータのセットを含む動的クライアント登録リクエストを受け取ります。登録が成功すると、実際に登録されたクライアントメタデータと新規に発行されたクライアント識別子が、JSON形式でエンドポイントから返却されます。後で使用するので、アプリケーションは発行されたクライアント識別子を覚えておかなければなりません。

![dynamic_client_registration.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/7aa1e357-74aa-5244-62e7-48a389441f67.png)

許可を受けていない者がクライアントを登録することを防ぐため、実世界の運用では何らかの対策を講じます。一つの例は、信頼できる機関から発行された『ソフトウェアステートメント』を動的クライアント登録リクエストが含むことを要求する方法です。英国スタイルのエコシステムでは、中央の『ディレクトリ』がこの役目を担います。

『[Implementer's note about Open Banking Brasil](https://darutk.medium.com/implementers-note-about-open-banking-brasil-78d3d612dfaf)』から抜粋した次の図は、ブラジルのオープンバンキングにおけるソフトウェアステートメントの発行と検証の流れを示しています。提示されたソフトウェアステートメントを検証することにより、動的クライアント登録リクエストが許可を受けた者からのものであることを認可サーバーは確認できます。

![software_statement_verification.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/ea90a3af-a681-e97d-20b1-4a4964382353.png)

# 設計上の考慮事項

## 中央管理されない信頼基盤

GAINが達成したい目標の一つは、既存のエコシステム群および将来のエコシステム群をつなぎ、異なるエコシステムにあるアプリケーションとサービスのやりとりを可能とする、国境をまたぐネットワークを構築することです。

その目標のための単純で分かりやすいアーキテクチャは、世界中の全てのアリケーションと全てのサービスを管理する単一の中央ディレクトリかもしれません。しかし、各エコシステムはそれぞれの必要性に応じて個別に規制を設けているため、そのアーキテクチャは非現実的です。エコシステムに対する影響は、あったとしても最小限でなければなりません。

異なるエコシステムにあるアプリケーションとサービス間の信頼は、単一の中央機関を必要とせずに構築可能であるべきです。

## 世界で一意となるクライアント識別子

動的クライアント登録を通じて発行されるクライアント識別子は、それを発行した認可サーバー内においてのみ一意です。同一のエコシステム内でさえ、異なる認可サーバーによって発行されたクライアント識別子群は重複する可能性があります。

そのため、既存のエコシステムのアプリケーションは、どのクライアント識別子がどの認可サーバーによって発行されたかを慎重に管理しなければなりません。アプリケーションは、やりとりしたいサービスに応じて、クライアント識別子を切り替えなければなりません。アプリケーション側にとって、これは負担となります。

どのサービスとやりとりする場合でも同一のクライアント識別子をアプリケーションが使えるなら、それが理想的です。

## KYC

高信頼性デジタルアイデンティティのためには、(姓名や誕生日といった) ユーザー属性がKYCを経て得られたものであることが保証されるべきです。

# 採用した標準

上記の考慮事項を踏まえ、コミュニティーグループはPoCのために次の仕様を採用することを決めました。

- [OpenID Connect Federation 1.0](https://openid.net/specs/openid-connect-federation-1_0.html)
- [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html)

## OpenID Connect Federation 1.0

OpenID Connect Federation 1.0 (OIDC Federation) はPoCにとって魅力的な次の特徴を持っています。

まず第一に、同仕様は、直接の関係を持たないアイデンティティプロバイダ (認可サーバー) とリライングパーティーが『トラストチェーン』に基づいてお互いを信頼し、リライングパーティーの事前登録を必要とせずにアイデンティティプロバイダがリライングパーティーからのOAuth/OIDCリクエストを受け取る仕組みを定義しています。

![oidcfed_overview.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/efb09cd8-8aa0-6a97-1dfa-37271d08d7ed.png)

トラストチェーンの信頼性は、アイデンティティプロバイダやリライングパーティーから独立している『トラストアンカー』と『中間オーソリティ』により担保されます。OIDC Federationのトラストチェーンは、公開鍵基盤の証明書チェーンに似ています。トラストアンカーと中間オーソリティは、概念的にはルート認証局、中間認証局と同じです。

次に、異なるサービスとの全ての通信においても、アプリケーションは一貫して同じクライアント識別子を使うことができます。クライアント識別子はグローバルに一意となるURLです。このURLはアイデンティティプロバイダによって決められるものではなく、リライングパーティーを管理する者によって決められます。OIDC Federationのクライアント識別子は、識別される対象自身によって識別子が決められる『[Decentralized Identifiers](https://www.w3.org/TR/did-core/)』との共通点があります。

## OpenID Connect for Identity Assurance 1.0

世の中のソーシャルメディアは (姓名や誕生日といった) ユーザー属性を保持しているかもしれませんが、たいていの場合、それらの属性の値は自己申告されたものに過ぎません。そのため、それらを法的な文脈で用いることができません。例えば、(GAINのホワイトペーパーでも言及されているユースケースでもある) アルコール飲料購入時の年齢確認の用途で自己申告された誕生日を用いることはできません。

OpenID Connect for Identity Assurance 1.0 (OIDC4IDA) は、パスポートや運転免許証などの公的証拠によって検証されたユーザー属性情報を転送する仕組みを定義しています。OIDC4IDAをサポートするアイデンティティプロバイダは、検証済みのユーザー属性を[IDトークン](https://openid.net/specs/openid-connect-core-1_0.html#IDToken)や[ユーザー情報エンドポイント](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo)からのレスポンスに埋め込むことができます。

技術的には、検証済みのユーザー属性に関する情報は全て`verified_claims`下に置かれます。そのため、IDトークンやユーザー情報レスポンスを受け取ったリライングパーティーは、検証済みのユーザー属性と (たいていは自己申告されたものに過ぎない) 通常のユーザー属性を区別することができます。

[eKYC-IDAワーキンググループ](https://openid.net/wg/ekyc-ida/)のWebページから抜粋した次の図は、`verified_claims`の内容がどのような構造になっているかを示しています。

![eKYC-IDA-WG-page-graphic-768x440_transparent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/e41ebea3-e438-cac0-be2a-9e91803b4a76.png)

# 認可フロー

次の図はPoCで用いた認可フローの要約です。

![authorization_flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/9558b347-7c4c-9637-583a-6b9ceec3af27.png)

注目すべき技術的なポイントは次の通りです。

1. 認可フローは[PAR](https://www.rfc-editor.org/rfc/rfc9126.html "RFC 9126: OAuth 2.0 Pushed Authorization Requests")リクエストから始まっています。リクエストに含まれるクライアント識別子がグローバルに一意なURLとなっています。
2. PARリクエストが`claims`リクエストパラメーターを含んでいます。トークンエンドポイントから発行されるIDトークンに検証済みのユーザー属性を埋め込むように認可サーバーに要求するため、パラメーターの値に`"id_token":{"verified_claims":...}`が含められています。
3. トークンエンドポイントから発行されるIDトークンのペイロードに`verified_claims`が含まれています。

## 認可フローの例

この節では、実際のリクエストとレスポンスを示すため、認可フローの例を記述します。

### 変数

例で用いられる共通の変数を次のように定義します。

| 変数名 | 値 |
|:--|:--|
| `ISSUER` | `https://fapidev-as.authlete.net/` |
| `PAR_ENDPOINT` | `https://fapidev-as.authlete.net/api/par` |
| `TOKEN_ENDPOINT` | `https://fapidev-as.authlete.net/api/token` |
| `CLIENT_ID` | `https://relying-party.authlete.net/5899463614448063` |
| `REDIRECT_URI` | `https://fapidev-api.authlete.net/api/mock/redirection/11794872185` |
| `AUTH_PRI` | `credentials/authlete/auth.pri.jwk` |
| `MTLS_PRI` | `credentials/authlete/mtls.pri.pem` |
| `MTLS_CER` | `credentials/authlete/mtls.cer.pem` |

PoCにとって重要なのは、クライアント識別子 (`CLIENT_ID`の値) がグローバルに一意なURLであることです。上の表内において相対パス (`AUTH_PRI`, `MTLS_PRI`, `MTLS_CER`の値) で指される実際のファイル群は[GAIN PoCのGitリポジトリ](https://bitbucket.org/openid/gain-poc/)内にあります。

### PARリクエスト

`private_key_jwt` クライアント認証 ([RFC 7523](https://www.rfc-editor.org/rfc/rfc7523.html)) のため、クライアントアサーションを用意します。下記のコマンドラインで用いている `generate-client-assertion.rb` スクリプトはGAIN PoCのGitリポジトリ内にあります。

```shell
ASSERTION=`bin/generate-client-assertion.rb --aud=$ISSUER --key=$AUTH_PRI --sub=$CLIENT_ID`
```

PARエンドポイントにPARリクエストを送ります。下記のコマンドライン内の改行は見やすくするためだけのものです。実際のリクエストに改行は含まれません。

```shell
curl $PAR_ENDPOINT
  -d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
  -d client_assertion=$ASSERTION
  -d client_id=$CLIENT_ID
  -d response_type=code
  -d scope=openid
  -d redirect_uri=$REDIRECT_URI
  --data-urlencode claims='{
    "id_token":{
      "verified_claims":{
        "verification":{
          "trust_framework":null
        },
        "claims":{
          "given_name":null,
          "family_name":null,
          "birthdate":null,
          ":age_18_or_over":null,
          "::age_100_or_over":null
        }
      }
    },
    "transformed_claims":{
      "age_18_or_over":{
        "claim":"birthdate",
        "fn":[ "years_ago", ["gte",18] ]
      }
    }
  }'
```

PARエンドポイントは次のようなレスポンスを返します。

```json
{
  "expires_in": 90,
  "request_uri":
    "urn:ietf:params:oauth:request_uri:20auKsHjnMxQg1L23jvvbCDvwlH6MAQk5cTQDO0gPaE"
}
```

PARレスポンス内の`request_uri`パラメーターの値は後続の認可リクエストで用いられます。

### 認可リクエスト

Webブラウザを介して認可エンドポイントに認可リクエストを送ります。

```shell
https://fapidev-www.authlete.net/api/authorization?client_id=${CLIENT_ID}&request_uri=${REQUEST_URI}
```

認可エンドポイントは認可ページを返します。ログインIDとパスワードとして`inga`/`inga`を入力し (このログイン情報はデモ用のものです)、続けて『Authorize』ボタンを押します。ログインフォームを再度表示する必要がある場合は、認可リクエストの末尾に`&prompt=login`をつけてください。

### トークンリクエスト

トークンエンドポイントにトークンリクエストを送ります。下記のコマンドライン内の改行は見やすくするためだけのものです。実際のリクエストに改行は含まれません。証明書バインディング ([RFC 8705](https://www.rfc-editor.org/rfc/rfc8705.html)) のため、例では`--key`オプションと`--cert`オプションを用いてクライアント証明書をリクエストに含めているので注意してください。

```shell
curl
  --key $MTLS_PRI
  --cert $MTLS_CER
  $TOKEN_ENDPOINT
  -d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
  -d client_assertion=$ASSERTION
  -d grant_type=authorization_code
  -d code=$AUTHORIZATION_CODE
  -d redirect_uri=$REDIRECT_URI
```

トークンエンドポイントは次のようなレスポンスを返します。`access_token`の値と`id_token`の値に含まれる改行は見やすくするためだけのもので、実際には含まれません。

```json
{
  "access_token":
    "eyJhbGciOiJQUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6ImF1dGhsZXRlLWZhcGlkZXYtYXBpLTI
     wMTgwNTI0In0.eyJzdWIiOiIxMDA0IiwiZ3JhbnRfdHlwZSI6ImF1dGhvcml6YXRpb25fY29kZSIsIn
     Njb3BlIjoib3BlbmlkIiwiYXV0aF90aW1lIjoxNjcwNjA3MTM1LCJpc3MiOiJodHRwczovL2ZhcGlkZ
     XYtYXMuYXV0aGxldGUubmV0LyIsImNuZiI6eyJ4NXQjUzI1NiI6ImhaT05oU01pbE12SkFDLXN5RFlY
     Uk9hOVlIYk8yUXR5bWxSME1LT09HLVUifSwiZXhwIjoxNjcwNjA5NTEyLCJpYXQiOjE2NzA2MDg2MTI
     sImNsaWVudF9pZCI6Imh0dHBzOi8vcmVseWluZy1wYXJ0eS5hdXRobGV0ZS5uZXQvNTg5OTQ2MzYxND
     Q0ODA2MyIsImp0aSI6IlNsM0VEUGpyTXllQkFyaU9NWlFmSjRBSXVkaldNd0lubVR5ck5wajRhN3cif
     Q.UQu-kO8yo--3dxiiaO2eEnBwNeBxL5LFepzxQWJp8G6k1L0NeVzgF1jMrWfKUSQishnKSSbnQ6ymD
     jQJ3E7MfJj4vdS8IdMf7wcCsMFTR0UXCpueKgOdXZmWAjU1IWM9rn5rz0jtlw-wNujLlT5sGearta3y
     8yyvQPgCe7exjdLstwF7Mo6XzXFx28FiZgrLiEouOYOP26QvRcptGeX6kQxiAIeWAxB4YFxlrZgJgmf
     uuL-mdx4uKp1jkxl0GQ5QZh7MUIvgAlm3od3CMPVDAeSdHQsnIoQPibTbzd069gMqqR9esV-j-x6_OX
     Lv8e1hmlAJ-DjW9vj07BjrQUGvFg",
  "refresh_token":"b172VT2h31P77-0HIiZ_2Nz6844qpZGozyqDFLigpV0",
  "scope":"openid",
  "id_token":
    "eyJraWQiOiJhdXRobGV0ZS1mYXBpZGV2LWFwaS0yMDE4MDUyNCIsImFsZyI6IlJTMjU2In0.eyJpc3M
     iOiJodHRwczovL2ZhcGlkZXYtYXMuYXV0aGxldGUubmV0LyIsInN1YiI6IjEwMDQiLCJhdWQiOlsiaH
     R0cHM6Ly9yZWx5aW5nLXBhcnR5LmF1dGhsZXRlLm5ldC81ODk5NDYzNjE0NDQ4MDYzIl0sImV4cCI6M
     TY3MDYwODkxMiwiaWF0IjoxNjcwNjA4NjEyLCJhdXRoX3RpbWUiOjE2NzA2MDcxMzUsInZlcmlmaWVk
     X2NsYWltcyI6eyJ2ZXJpZmljYXRpb24iOnsidHJ1c3RfZnJhbWV3b3JrIjoibmlzdF84MDBfNjNBIn0
     sImNsYWltcyI6eyJnaXZlbl9uYW1lIjoiSW5nYSIsImZhbWlseV9uYW1lIjoiU2lsdmVyc3RvbmUiLC
     JiaXJ0aGRhdGUiOiIxOTkxLTExLTA2IiwiOjphZ2VfMTAwX29yX292ZXIiOmZhbHNlLCI6YWdlXzE4X
     29yX292ZXIiOnRydWV9fX0.PSf0WD8j92e080Jue2LmDeHV7_027OAw9hbnmnJyd26YiA7DCOTiLhlz
     mKgN_7CPOySkU5I-AWaPeDblVLSrpO1CQpp_GyYHETffC-ew2fIFls59xTUgC81_dBs011VDA95nZc9
     E3VjrRVcnBrmMWNNaw2plfb6ooXDLj2tTpIoYl_CMbp4eI_1rCo0G0yyZXd3Vw6bTWYKdG33-nnJ7AL
     O6KtnkcKuIVthFDBFkhA320ujUzopHn7-ksN1onQwQa9bxyFW7XPHp5C24TNsIkBP-an0emPJ0JsSYr
     YBZFtrxjbuUxZz16SpmTEj3bPZepsSGdfCZCLdbAP_QB4cAwQ",
  "token_type":"Bearer",
  "expires_in":900
}
```

上記例のIDトークンのペイロードは次のJSONと同等の情報を含んでいます。注目すべき点は、`verified_claims`が含まれていること (OIDC4IDA)、および、`aud`クレームにグローバルに一意なURLがリストされていること (OIDC Federation) です。

```json
{
  "iss": "https://fapidev-as.authlete.net/",
  "sub": "1004",
  "aud": [
    "https://relying-party.authlete.net/5899463614448063"
  ],
  "exp": 1670608912,
  "iat": 1670608612,
  "auth_time": 1670607135,
  "verified_claims": {
    "verification": {
      "trust_framework": "nist_800_63A"
    },
    "claims": {
      "given_name": "Inga",
      "family_name": "Silverstone",
      "birthdate": "1991-11-06",
      "::age_100_or_over": false,
      ":age_18_or_over": true
    }
  }
}
```

# トラストチェーン解決

認可リクエスト内のクライアント識別子が未知であるもののHTTPでアクセス可能なURLの場合、OIDC Federation仕様をサポートする認可サーバーはそのURLからトラストチェーンの構築を試み、当クライアント識別子が受け入れ可能であるか決定します。クライアント識別子が受け入れ可能であれば、認可サーバーはリライングパーティーを自動的に登録し、認可リクエストの処理を続けます。この処理は『自動クライアント登録』と呼ばれ、OpenID Connect Dynamic Client Registration 1.0やRFC 7591で定義されている『動的クライアント登録』とは異なるものです。

自動クライアント登録の処理は次のステップで構成されます。

1. リライングパーティーのトラストチェーン群を解決し、それらの中から一つ選ぶ。
2. もし定義されていれば、選択したトラストチェーンのメタデータポリシーを適用する。
3. 認可リクエストを認証する。
4. クライアントメタデータの有効性を確認する。
5. クライアントメタデータを用いてリライングパーティーを登録する。

最初のステップは『トラストチェーン解決』と呼ばれます。中央管理されない信頼基盤のコアとなる部分です。

## トラストチェーン解決の例

Authlete社のウェブサイト上の記事『[OpenID Connect Federation 1.0](https://www.authlete.com/developers/oidcfed/)』から抜粋した次の図は、トラストチェーン解決のフローを示しています。

![oidcfed_tcr.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/5201618c-36d4-84ba-5b69-a2aa1db3359c.png)

この節では仕様の詳細は説明しないものの、`https://relying-party.authlete.net/5899463614448063`を例に用いてトラストチェーン解決のために実際にやりとりされるメッセージを示します。

### リライングパーティーのエンティティコンフィギュレーション

最初のステップは、リライングパーティーの『エンティティコンフィギュレーション』を取得することです。エンティティコンフィギュレーションは、クライアント識別子と固定文字列`/.well-known/openid-federation`を連結して得られるURLからダウンロードできます。

```shell
curl https://relying-party.authlete.net/5899463614448063/.well-known/openid-federation
```

エンティティコンフィギュレーションはJWTの一種です。次のものは当URLから返されるレスポンスの例です。

```
eyJ0eXAiOiJlbnRpdHktc3RhdGVtZW50K2p3dCIsImFsZyI6IlJTMjU2Iiwia2lkIjoib3FfRzRWSS1XTnowdUtRUWVxNGhOdHNTMFpOck5JakpZeHZVU3N6ckZPcyJ9.eyJpc3MiOiJodHRwczovL3JlbHlpbmctcGFydHkuYXV0aGxldGUubmV0LzU4OTk0NjM2MTQ0NDgwNjMiLCJzdWIiOiJodHRwczovL3JlbHlpbmctcGFydHkuYXV0aGxldGUubmV0LzU4OTk0NjM2MTQ0NDgwNjMiLCJpYXQiOjE2Nzk3NTA0MjAsImV4cCI6MTY3OTc1NDAyMCwiandrcyI6eyJrZXlzIjpbeyJrdHkiOiJSU0EiLCJlIjoiQVFBQiIsIm4iOiJnay0zaG5jTHBnTVo3cDU2WkxLNTBLZHZzQXViVjkydnlYTnpBemZvZzV6UTREbUF5eV9xZ3VnN2g3VDBqOHhmZnkyU051UW1UOUdCNFUtZzJVYzVmdk9ydHJYdlBJSDhYVDhTeHJ5bTZXSmFVaHlnNjE1M0ZzTlpLMzdrTDZMM1JTaVl0WFA0N093TmNsX0xGaFVKbjdHN0FhR2p5VUIyMnVZWmdTZTE2RWhBejdrazJ1NHdmeFVhM0VxdjBIbjdtb1hDWUZaZUNxNHhaWDI2dWF0d1QzaTc1bWs2RFNKRVBKQ3hSWHRMU1FpRzFKajg5VVVicEZrRkRSQWd0WXh2MUowTldyNnlBRFdUMDRJNEgyTUswR1JLZEtFYkU4dG91bFYwdnJ4dE5ZemlGOGpBdllsMVMyQmpBaGFZbDh1YkFBV1V2MDBBa0dDZlY0VWZKWmtTQ1EiLCJraWQiOiJvcV9HNFZJLVdOejB1S1FRZXE0aE50c1MwWk5yTklqSll4dlVTc3pyRk9zIn1dfSwiYXV0aG9yaXR5X2hpbnRzIjpbImh0dHBzOi8vdHJ1c3QtYW5jaG9yLmF1dGhsZXRlLm5ldC8iXSwibWV0YWRhdGEiOnsib3BlbmlkX3JlbHlpbmdfcGFydHkiOnsiY2xpZW50X2lkIjoiaHR0cHM6Ly9yZWx5aW5nLXBhcnR5LmF1dGhsZXRlLm5ldC81ODk5NDYzNjE0NDQ4MDYzIiwicmVkaXJlY3RfdXJpcyI6WyJodHRwczovL2ZhcGlkZXYtYXBpLmF1dGhsZXRlLm5ldC9hcGkvbW9jay9yZWRpcmVjdGlvbi8xMTc5NDg3MjE4NSIsImh0dHA6Ly9sb2NhbGhvc3Q6MzAwMC95ZXMvb2lkY2NiIl0sInJlc3BvbnNlX3R5cGVzIjpbImNvZGUiLCJpZF90b2tlbiJdLCJncmFudF90eXBlcyI6WyJhdXRob3JpemF0aW9uX2NvZGUiLCJyZWZyZXNoX3Rva2VuIl0sImNsaWVudF9uYW1lIjoiR0FJTiBQb0MgQ2xpZW50IiwibG9nb191cmkiOiJodHRwczovL3d3dy5hdXRobGV0ZS5jb20vaW1nL2xvZ28vYXV0aGxldGUtbG9nby1pbWFnZS1ibHVlLXdpdGgtd2hpdGUtYmctMjU2LnBuZyIsImp3a3MiOnsia2V5cyI6W3sia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsImtpZCI6ImdhaW5wb2MiLCJ4NWMiOlsiTUlJQmp6Q0NBVFdnQXdJQkFnSVVTWEtHWlNSQzdLVkNEYUNmbkpLd0JUQ2ZzUkV3Q2dZSUtvWkl6ajBFQXdJd0hURWJNQmtHQTFVRUF3d1NZMnhwWlc1MExtVjRZVzF3YkdVdVkyOXRNQjRYRFRJeU1EVXdOREl6TVRjME5Gb1hEVEkxTURFeU9ESXpNVGMwTkZvd0hURWJNQmtHQTFVRUF3d1NZMnhwWlc1MExtVjRZVzF3YkdVdVkyOXRNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVBUDZQZ0ZsOU5uUVNhWUNOOG1TZ09aRWxsZWFwMHFPZythMzlIck11RU5UeGQ2NmRuS3pwY1lGV09LMGNxM1BGMEVsTGhTMzNwbmFya2RDd2Z5TnB4Nk5UTUZFd0hRWURWUjBPQkJZRUZBWlF1MmgyMVZwMC83MkxMRitNaXpqWXBsQjhNQjhHQTFVZEl3UVlNQmFBRkFaUXUyaDIxVnAwLzcyTExGK01pempZcGxCOE1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0NnWUlLb1pJemowRUF3SURTQUF3UlFJZ0JINldBeE5nSWZrOFg0WVVKRnJRR0tYZ2gwREh2QWhMVUs5THFBSjZBWFlDSVFDZmxFSU1BbmNVUGcxR29meGsxZ1RVZ3NMdUk5UUR2L1Y1WG1kS0lPbTd3UT09Il0sIngiOiJBUDZQZ0ZsOU5uUVNhWUNOOG1TZ09aRWxsZWFwMHFPZy1hMzlIck11RU5RIiwieSI6IjhYZXVuWnlzNlhHQlZqaXRIS3R6eGRCSlM0VXQ5NloycTVIUXNIOGphY2MifSx7Imt0eSI6IlJTQSIsImUiOiJBUUFCIiwia2lkIjoidmNJT2FVMFk4TjhXZDdIbjVuSzlxWEZ3WHhJbTVJMTB6NXlXUFdYMFk4MCIsImFsZyI6IlJTMjU2IiwibiI6InQzcDIzdzcwOFZ6OWtTQ1k4UG5CcUNndTl5Zi00TVNEOTRDN3JheU5uSElUSlhwNV9tVS1DdkdVekdEVFNUNUxnQWRXbG9zZEJQa0lhbHdYM1BmWFhYZllVT2dVZmk2LWlCVDhjLUNtNF9GLU9rYldlSmJpOU45S1NtSjRoVURNQzBoc2M1bVg4TkJiNWNueno5NnRFTjNtVENVY1gyY0VxVV82NmZxQy1kZkRtT2NObDJQeE82dVdBenpaVk9ibmxFZjlnbkg2cVlOVFpZcS0yXzZxT0JkTEkyNDNGTnhxUFZ6aFYtRldMRkMycUhlVVlOcGNuOWJLUjZnb1lCcFNJdnA1VnEwX0JaZEd3TTVudkhVaVFvYVdwUDlacUJ2SUF6VWpCVjlOMW9yZXZ4enVsdl9sdjB3aElnYllXXzlNX3Q5SVI0ekdtb0NNcmhVaUxBUmsyUSJ9XX0sInN1YmplY3RfdHlwZSI6InB1YmxpYyIsImlkX3Rva2VuX3NpZ25lZF9yZXNwb25zZV9hbGciOiJSUzI1NiIsInRva2VuX2VuZHBvaW50X2F1dGhfbWV0aG9kIjoicHJpdmF0ZV9rZXlfand0IiwidG9rZW5fZW5kcG9pbnRfYXV0aF9zaWduaW5nX2FsZyI6IlJTMjU2IiwidGxzX2NsaWVudF9jZXJ0aWZpY2F0ZV9ib3VuZF9hY2Nlc3NfdG9rZW5zIjp0cnVlLCJjbGllbnRfcmVnaXN0cmF0aW9uX3R5cGVzIjpbImF1dG9tYXRpYyIsImV4cGxpY2l0Il19fX0.bTpkHrvRqscNmlqJXnxQk_YLjde6H7VFEKBfGoBOI0vWCYk4gLbImbrJS4-z_aWl8xqb9srFjs2Mzlq8t-8RNJdSxJhrI_ha9SUkZITxIGyR1lkTp4wGwfeZsQIt4mocEKv_iIV64Km9KLl7ELaXj1ipegFuFu5BBgvEYHCmSuMcjvPLIYI4bK224xbVreGIyyhu3dPyzBfLcUQ767RiaViQrnTzfZIAOmbT_a8j_yy1rSnqeM7O-33VNayT8c7H-2LjZntDMVSqgaVZox0PsqNaLyQX-ii6UPeje5ESvTkO54-_0ZVwx01b5cfAXfpUdSfNBGCYNoiDbFfaEUz5vg
```

このエンティティコンフィギュレーションのペイロードには次のクレームが含まれています。

```json
"authority_hints": [
  "https://trust-anchor.authlete.net/"
]
```

`authority_hints`は、リライングパーティーのために『エンティティステートメント』を発行してくれる直接の上位オーソリティ (トラストアンカーまたは中間オーソリティ) 群のリストです。この例では`authority_hints`クレームには要素が一つだけ含まれています。

要素の値によりそれがトラストアンカーであることが示唆されていますが、この時点では、それがトラストアンカーなのか中間オーソリティなのかは未知です。いずれにしても、それはリライングパーティーの直接の上位オーソリティです。

### 上位オーソリティのエンティティコンフィギュレーション

上位オーソリティにリライングパーティーのエンティティステートメントの発行を依頼するためには、エンティティステートメントを発行するWeb APIのURLを知る必要があります。そのWeb APIは『フェッチエンドポイント』と呼ばれます。

フェッチエンドポイントのURLは上位オーソリティのエンティティコンフィギュレーション内に書かれているので、そのエンティティコンフィギュレーションをダウンロードする必要があります。

```shell
curl https://trust-anchor.authlete.net/.well-known/openid-federation
```

次のものは、上位オーソリティのエンティティコンフィギュレーションの例です。

```
eyJhbGciOiJSUzI1NiIsImtpZCI6Ik45WWhJRjNtQTRRV3phM0ZYOEN6cmdod3VfdEhmaG03N3Y1MFdkREg2YTQiLCJ0eXAiOiJlbnRpdHktc3RhdGVtZW50K2p3dCJ9.eyJleHAiOjE2Nzk3NTUyMzcsImlhdCI6MTY3OTc1MTYzNywiaXNzIjoiaHR0cHM6Ly90cnVzdC1hbmNob3IuYXV0aGxldGUubmV0LyIsInN1YiI6Imh0dHBzOi8vdHJ1c3QtYW5jaG9yLmF1dGhsZXRlLm5ldC8iLCJqd2tzIjp7ImtleXMiOlt7Imt0eSI6IlJTQSIsIm4iOiJtZ0Zmd09mcmN4eGU5OWlsTUdiVzFtdWlzczF1ZWdjSjNGaks0ajdfUHhxbF91YmE1cWdkUXBwSDNUT0dqMXYtME1hQXZMUFpFaXlIT0VEUzRtN2ptR0Nac1k0OXU0REFrbUd3VFZIQXh4ZHg0NlZBd0d2WVJDdUl3emEtMXBZOHVNVXJSbHlBblY0R0pkSF9EWDdBX3hsWG9aMk9XT1gwMW93N3dLeEFYRHc2SFlpMy1iYldJOExPTWhmYXNJc2ttMWJDSVVjMXdXaEM5N1RMN0VheFZvQ0paaDM0bXdIRFhCVTNlNnRvUXJYVnZzM2ZaQWh4MllJSzhxUEVVQmtxZmxIVGJfY1ltSkNHeW5TSTZLSTZ5WkhJOUdfa1dwZUpTRkt3eExZWlNOVlVuajl0ZGpyVW9PRVU1MXFlTUZybVotVm9tVTFaaEFaU09TdF9icGx2bHciLCJlIjoiQVFBQiIsImtpZCI6Ik45WWhJRjNtQTRRV3phM0ZYOEN6cmdod3VfdEhmaG03N3Y1MFdkREg2YTQifV19LCJtZXRhZGF0YSI6eyJmZWRlcmF0aW9uX2VudGl0eSI6eyJmZWRlcmF0aW9uX2xpc3RfZW5kcG9pbnQiOiJodHRwczovL3RydXN0LWFuY2hvci5hdXRobGV0ZS5uZXQvbGlzdC8iLCJmZWRlcmF0aW9uX2ZldGNoX2VuZHBvaW50IjoiaHR0cHM6Ly90cnVzdC1hbmNob3IuYXV0aGxldGUubmV0L2ZldGNoLyJ9fX0.JHEESDp5xpkFAhPsORpMeJXILAT-y8iZhbYUWqnwSiL5H2ewxJx368e457ODB_Ep8OHghMNpCtEpzZy82ePGElcDGvFPLB6lCpR_Vu5AlA18eFSo-m8B15CComwpSDBM2n0objVgyufsMedq1e7hRbQXtgwBOtKR3WDXjD-RQxUVWJ_eMGgL1zhj09jmbCuPY0xxkoi-hwMmsPYi-kA-fRLIKksHyiprQkRE_gaDQMgSRjdJFdpwGU4bHs0fwGX_9U7xNtdEbx0YYdsPfRxC_cKNTnZ378jNAQ9Dn6McHkIGgV3o-8VdNaSh9_5mAoINYls7Ze4nDoNDwPxNZyRW3Q
```

このエンティティコンフィギュレーションのペイロードには次のクレームが含まれています。

```json
"metadata": {
  "federation_entity": {
    "federation_list_endpoint": "https://trust-anchor.authlete.net/list/",
    "federation_fetch_endpoint": "https://trust-anchor.authlete.net/fetch/"
  }
}
```

`federation_fetch_endpoint`プロパティの値がフェッチエンドポイントのURLです。

### 上位オーソリティが発行するリライングパーティーのエンティティステートメント

フェッチエンドポイントは`sub`リクエストパラメーターで指定されたエンティティのエンティティステートメントを、そのエンティティを認識している場合に限り、発行します。

```shell
curl https://trust-anchor.authlete.net/fetch/?sub=https://relying-party.authlete.net/5899463614448063
```

次のものはフェッチエンドポイントからのレスポンスの例です。

```
eyJhbGciOiJSUzI1NiIsImtpZCI6Ik45WWhJRjNtQTRRV3phM0ZYOEN6cmdod3VfdEhmaG03N3Y1MFdkREg2YTQiLCJ0eXAiOiJlbnRpdHktc3RhdGVtZW50K2p3dCJ9.eyJleHAiOjE2Nzk5MjU0NDEsImlhdCI6MTY3OTc1MjY0MSwiaXNzIjoiaHR0cHM6Ly90cnVzdC1hbmNob3IuYXV0aGxldGUubmV0LyIsInN1YiI6Imh0dHBzOi8vcmVseWluZy1wYXJ0eS5hdXRobGV0ZS5uZXQvNTg5OTQ2MzYxNDQ0ODA2MyIsImp3a3MiOnsia2V5cyI6W3siZSI6IkFRQUIiLCJuIjoiZ2stM2huY0xwZ01aN3A1NlpMSzUwS2R2c0F1YlY5MnZ5WE56QXpmb2c1elE0RG1BeXlfcWd1ZzdoN1Qwajh4ZmZ5MlNOdVFtVDlHQjRVLWcyVWM1ZnZPcnRyWHZQSUg4WFQ4U3hyeW02V0phVWh5ZzYxNTNGc05aSzM3a0w2TDNSU2lZdFhQNDdPd05jbF9MRmhVSm43RzdBYUdqeVVCMjJ1WVpnU2UxNkVoQXo3a2sydTR3ZnhVYTNFcXYwSG43bW9YQ1lGWmVDcTR4WlgyNnVhdHdUM2k3NW1rNkRTSkVQSkN4Ulh0TFNRaUcxSmo4OVVVYnBGa0ZEUkFndFl4djFKME5XcjZ5QURXVDA0STRIMk1LMEdSS2RLRWJFOHRvdWxWMHZyeHROWXppRjhqQXZZbDFTMkJqQWhhWWw4dWJBQVdVdjAwQWtHQ2ZWNFVmSlprU0NRIiwiYWxnIjoiUlMyNTYiLCJraWQiOiJvcV9HNFZJLVdOejB1S1FRZXE0aE50c1MwWk5yTklqSll4dlVTc3pyRk9zIiwia3R5IjoiUlNBIn1dfSwibWV0YWRhdGFfcG9saWN5Ijp7Im9wZW5pZF9yZWx5aW5nX3BhcnR5Ijp7fX0sInRydXN0X21hcmtzIjpbeyJpZCI6Imh0dHBzOi8vcmVseWluZy1wYXJ0eS5hdXRobGV0ZS5uZXQvNTg5OTQ2MzYxNDQ0ODA2MyIsInRydXN0X21hcmsiOiJleUpoYkdjaU9pSlNVekkxTmlJc0ltdHBaQ0k2SWs0NVdXaEpSak50UVRSUlYzcGhNMFpZT0VONmNtZG9kM1ZmZEVobWFHMDNOM1kxTUZka1JFZzJZVFFpTENKMGVYQWlPaUowY25WemRDMXRZWEpySzJwM2RDSjkuZXlKemRXSWlPaUpvZEhSd2N6b3ZMM0psYkhscGJtY3RjR0Z5ZEhrdVlYVjBhR3hsZEdVdWJtVjBMelU0T1RrME5qTTJNVFEwTkRnd05qTWlMQ0pwYzNNaU9pSm9kSFJ3Y3pvdkwzUnlkWE4wTFdGdVkyaHZjaTVoZFhSb2JHVjBaUzV1WlhRdklpd2lhV0YwSWpveE5qYzVOelV5TmpReGZRLlJmNHV0Y24wa3dfTWVSXzdrU1lETFZyeEpqUWJ5TnZZQ2FVbmpkVWZmU2xoT2xxX0dCdTU3bE9uSGNJLWNEdzB1TFdqZVNPLUptdmxRY2t2am94YW1HclVRaFlKUjBLdFRBNjFVS1lhVTJjLTNrRDNkTV85SElIdmg5UjQ5cVpjTVlGbXFrMGt2UmVSNERITXRzd3d5WGRGbUxhOG93dk9PTzZvVnZXcmQxTm11QTZfUU4tNWFxVUpFUk9KNFUxbTMzbnRUaEpXSWdGS0NBRTBPVkZCaUlKN2F1bFdFSWo2YUNJVDF6eHI0RUpibUJqc2FUNlpRMnM3U1FncEZDbU0tei1nVUtOV1JsN0NHWDVWc1JrNVR5aVNlNDhpRE9HdzlwOUVia195eVgxWU9vNE9TMEhBeUlsTkd3cDNlUEh1SmhKQUFmUVNxMUt2aG1EN0N2U3R1dyJ9XX0.PMuzOEOhe0R2GEZr7dmZCFHbzd3CFz-iJQoXGXOEPXkPCTN38GUAr90k98phyM9FQDlj9XvUWDcIQTgpr83xU6lwXx2Pqf1VbKabdwsBH5ES0n0LSDTboG6gNU-t2xOiaznMEorhM8JyG7SX4PWSRzrDr6FHqJVQShzCJY5D_3CDF0YYtJf4gR0g9tgpZu6MnWZh1rargS_Q2qg1X2IgBixBloNVnpqpIF3FGQjmGqzOeepG0EseZBj-gMUTCpccgf5_5nmY3zhUdetEniQo-gHOezSnnWH3BLWzO3cfHKkm589A5lE10qiq9db-p386e0-RRzF_M3CoQJE4uoJwAw
```

### トラストチェーン検証

上位オーソリティ (`https://trust-anchor.authlete.net/`) のエンティティコンフィギュレーションが`authority_hints`クレームを含んでいないので、当上位オーソリティはトラストアンカーです。そのため、トラストチェーン解決の処理はここで終了します。

結果として、この例で用いたトラストチェーンは2つのJWTのみで構築されました。最初のJWTはリライングパーティーのエンティティコンフィギュレーションで、最後のJWTはトラストアンカーのフェッチエンドポイントが発行したエンティティステートメントです。

認可サーバーが当トラストアンカーを信頼済みのものであると認識しており、かつ、トラストチェーン解決の過程で登場した全てのJWTの署名が検証されれば、そのトラストチェーンは有効であるとみなされます。

署名検証に用いる公開鍵は、直接の上位オーソリティが発行するエンティティステートメントに含まれる点に注意してください。下図が示す通り、その関係は複雑です。

![oidcfed_tc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/039a213f-ea2a-3216-4bdf-3ac0d070b1b0.png)