> ## Documentation Index
> Fetch the complete documentation index at: https://developers.authlete.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 標準仕様による徹底的な API 保護

> RFC 6749 以降に開発された標準仕様をフル活用して API を保護する方法をご紹介します。

## はじめに

Internet Engineering Task Force ([IETF][IETF]) が OAuth 2.0 の中心仕様である
[RFC 6749][RFC_6749] を公開したのは 2012 年 10 月 13 日です。IETF の
[OAUTH ワーキンググループ][IETF_OAUTH_WG]はその後も活動を続け、現在も活発に新しい標準仕様の策定作業をおこなっています。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/ietf_security_area.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=c24d819a6739839e37262cfbdc7816d9" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/ietf_security_area.png" />

年月の経過に伴い、同ワーキンググループによる RFC も増えていき、今では 30 を超えています。

|              |    日付   |          RFC         | タイトル                                                                                                                           |
| :----------: | :-----: | :------------------: | :----------------------------------------------------------------------------------------------------------------------------- |
|       1      | 2012-10 | [RFC 6749][RFC_6749] | [The OAuth 2.0 Authorization Framework][RFC_6749]<sup>★★★</sup>                                                                |
|       2      | 2012-10 | [RFC 6750][RFC_6750] | [The OAuth 2.0 Authorization Framework: Bearer Token Usage][RFC_6750]<sup>★★★</sup>                                            |
|       3      | 2012-10 | [RFC 6755][RFC_6755] | [An IETF URN Sub-Namespace for OAuth][RFC_6755]                                                                                |
|       4      | 2013-01 | [RFC 6819][RFC_6819] | [OAuth 2.0 Threat Model and Security Considerations][RFC_6819]                                                                 |
|       5      | 2013-08 | [RFC 7009][RFC_7009] | [OAuth 2.0 Token Revocation][RFC_7009]                                                                                         |
|       6      | 2015-05 | [RFC 7519][RFC_7519] | [JSON Web Token (JWT)][RFC_7519]<sup>★★★</sup>                                                                                 |
|       7      | 2015-05 | [RFC 7521][RFC_7521] | [Assertion Framework for OAuth 2.0 Client Authentication and Authorization Grants][RFC_7521]                                   |
|       8      | 2015-05 | [RFC 7522][RFC_7522] | [Security Assertion Markup Language (SAML) 2.0 Profile for OAuth 2.0 Client Authentication and Authorization Grants][RFC_7522] |
|       9      | 2015-05 | [RFC 7523][RFC_7523] | [JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants][RFC_7523]<sup>★★</sup>             |
|      10      | 2015-07 | [RFC 7591][RFC_7591] | [OAuth 2.0 Dynamic Client Registration Protocol][RFC_7591]<sup>★</sup>                                                         |
|      11      | 2015-07 | [RFC 7592][RFC_7592] | [OAuth 2.0 Dynamic Client Registration Management Protocol][RFC_7592]                                                          |
|      12      | 2015-09 | [RFC 7636][RFC_7636] | [Proof Key for Code Exchange by OAuth Public Clients][RFC_7636]<sup>★★</sup>                                                   |
|      13      | 2015-10 | [RFC 7662][RFC_7662] | [OAuth 2.0 Token Introspection][RFC_7662]<sup>★★</sup>                                                                         |
|      14      | 2016-04 | [RFC 7800][RFC_7800] | [Proof-of-Possession Key Semantics for JSON Web Tokens (JWTs)][RFC_7800]                                                       |
|      15      | 2017-06 | [RFC 8176][RFC_8176] | [Authentication Method Reference Values][RFC_8176]                                                                             |
|      16      | 2017-10 | [RFC 8252][RFC_8252] | [OAuth 2.0 for Native Apps][RFC_8252]                                                                                          |
|      17      | 2018-06 | [RFC 8414][RFC_8414] | [OAuth 2.0 Authorization Server Metadata][RFC_8414]<sup>★</sup>                                                                |
|      18      | 2019-08 | [RFC 8628][RFC_8628] | [OAuth 2.0 Device Authorization Grant][RFC_8628]                                                                               |
|      19      | 2020-01 | [RFC 8693][RFC_8693] | [OAuth 2.0 Token Exchange][RFC_8693]                                                                                           |
|      20      | 2020-02 | [RFC 8705][RFC_8705] | [OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens][RFC_8705]<sup>★★</sup>                        |
|      21      | 2020-02 | [RFC 8707][RFC_8707] | [Resource Indicators for OAuth 2.0][RFC_8707]                                                                                  |
|      22      | 2020-02 | [RFC 8725][RFC_8725] | [JSON Web Token Best Current Practices][RFC_8725]                                                                              |
|      23      | 2021-10 | [RFC 9068][RFC_9068] | [JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens][RFC_9068]                                                           |
|      24      | 2021-08 | [RFC 9101][RFC_9101] | [The OAuth 2.0 Authorization Framework: JWT-Secured Authorization Request (JAR)][RFC_9101]<sup>★★</sup>                        |
|      25      | 2021-08 | [RFC 9126][RFC_9126] | [OAuth 2.0 Pushed Authorization Requests][RFC_9126]<sup>★★</sup>                                                               |
|      26      | 2022-03 | [RFC 9207][RFC_9207] | [OAuth 2.0 Authorization Server Issuer Identification][RFC_9207]                                                               |
|      27      | 2022-08 | [RFC 9278][RFC_9278] | [JWK Thumbprint URI][RFC_9278]                                                                                                 |
|      28      | 2023-05 | [RFC 9396][RFC_9396] | [OAuth 2.0 Rich Authorization Requests][RFC_9396]<sup>★</sup>                                                                  |
|      29      | 2023-09 | [RFC 9449][RFC_9449] | [OAuth 2.0 Demonstrating Proof of Possession (DPoP)][RFC_9449]<sup>★★</sup>                                                    |
|      30      | 2023-09 | [RFC 9470][RFC_9470] | [OAuth 2.0 Step Up Authentication Challenge Protocol][RFC_9470]                                                                |
|      31      | 2025-01 | [RFC 9700][RFC_9700] | [Best Current Practice for OAuth 2.0 Security][RFC_9700]                                                                       |
|      32      | 2025-01 | [RFC 9701][RFC_9701] | [JSON Web Token (JWT) Response for OAuth Token Introspection][RFC_9701]                                                        |
|      33      | 2025-04 | [RFC 9728][RFC_9728] | [OAuth 2.0 Protected Resource Metadata][RFC_9728]<sup>★</sup>                                                                  |
|              |         |                      |                                                                                                                                |
| 2025 年 7 月現在 |         |                      |                                                                                                                                |
|              |         |                      |                                                                                                                                |

一方、OpenID Foundation ([OIDF][OIDF]) が OpenID Connect の中心仕様である
[OpenID Connect Core 1.0][OIDC_CORE] を公開したのは 2014 年です。
同団体のワーキンググループ群は、現在も引き続き標準仕様策定作業をおこなっています。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/oidf_working_groups.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=4073d6cd9f6666a9740b884310c34d16" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/oidf_working_groups.png" />

OIDF のワーキンググループ群が策定した、もしくは策定作業中の仕様群も、それなりの数があります。

|                            WG                           | Status | Title                                                                                                                        |
| :-----------------------------------------------------: | :----: | :--------------------------------------------------------------------------------------------------------------------------- |
|                [AB/Connect][OIDF_ABC_WG]                |  Final | [OpenID Connect Core 1.0][OIDC_CORE]<sup>★★★</sup>                                                                           |
|                [AB/Connect][OIDF_ABC_WG]                |  Final | [OpenID Connect Discovery 1.0][OIDC_DISCOVERY]<sup>★★</sup>                                                                  |
|                [AB/Connect][OIDF_ABC_WG]                |  Final | [OpenID Connect Dynamic Client Registration 1.0][OIDC_DCR]<sup>★</sup>                                                       |
|                [AB/Connect][OIDF_ABC_WG]                |  Final | [OAuth 2.0 Multiple Response Type Encoding Practices][MULTI_RES_TYPES]<sup>★★</sup>                                          |
|                [AB/Connect][OIDF_ABC_WG]                |  Final | [OAuth 2.0 Form Post Response Mode][FORM_POST]                                                                               |
|                [AB/Connect][OIDF_ABC_WG]                |  Final | [OpenID 2.0 to OpenID Connect Migration 1.0][OIDC_MIGRATION]                                                                 |
|                [AB/Connect][OIDF_ABC_WG]                |  Final | [OpenID Connect RP-Initiated Logout 1.0][OIDC_RP_LOGOUT]                                                                     |
|                [AB/Connect][OIDF_ABC_WG]                |  Final | [OpenID Connect Session Management 1.0][OIDC_SESSION]                                                                        |
|                [AB/Connect][OIDF_ABC_WG]                |  Final | [OpenID Connect Front-Chanel Logout 1.0][OIDC_FRONT_LOGOUT]                                                                  |
|                [AB/Connect][OIDF_ABC_WG]                |  Final | [OpenID Connect Back-Channel Logout 1.0][OIDC_BACK_LOGOUT]                                                                   |
|                [AB/Connect][OIDF_ABC_WG]                |  Final | [OpenID Connect Core Error Code unmet\_authentication\_requirements][UNMET_AUTH_REQ]                                         |
|                [AB/Connect][OIDF_ABC_WG]                |  Final | [Initiating User Registration via OpenID Connect 1.0][PROMPT_CREATE]                                                         |
|                [AB/Connect][OIDF_ABC_WG]                |   I/D  | [OpenID Federation 1.0][OIDFED]<sup>★</sup>                                                                                  |
|                [AB/Connect][OIDF_ABC_WG]                |   I/D  | [Self-Issued OpenID Provider v2][SIOPv2]                                                                                     |
|                [AB/Connect][OIDF_ABC_WG]                |   I/D  | [OpenID Connect Native SSO for Mobile Apps][NATIVESSO]                                                                       |
|                [AB/Connect][OIDF_ABC_WG]                |  Draft | [OpenID Connect Claims Aggregation][OIDC_CLAIMS]                                                                             |
|                [AB/Connect][OIDF_ABC_WG]                |  Draft | [OpenID Federation Extended Subordinate Listing 1.0][OIDFED_LISTING]                                                         |
|                [AB/Connect][OIDF_ABC_WG]                |  Draft | [OpenID Federation Wallet Architectures 1.0][OIDFED_WALLET]                                                                  |
|                [AB/Connect][OIDF_ABC_WG]                |  Draft | [OpenID Connect Relying Party Metadata Choices 1.0][RP_META_CHOICES]                                                         |
|                [AB/Connect][OIDF_ABC_WG]                |  Draft | [OpenID Provider Commands 1.0][PROVIDER_COMMANDS]                                                                            |
|                [AB/Connect][OIDF_ABC_WG]                |  Draft | [OpenID Connect Enterprise Extensions 1.0][OIDC_ENTERPRISE]                                                                  |
|                [AB/Connect][OIDF_ABC_WG]                |  Draft | [OpenID Connect Ephemeral Subject Identifier 1.0][OIDC_EPHEMERAL]                                                            |
|                 [AuthZEN][OIDF_AUTHZ_WG]                |   I/D  | [Authorization API 1.0 - version 01](https://openid.net/specs/authorization-api-1_0-01.html)                                 |
|                    [DCP][OIDF_DCP_WG]                   |  Final | [OpenID for Verifiable Presentations 1.0][OID4VP]<sup>★★</sup>                                                               |
|                    [DCP][OIDF_DCP_WG]                   |   I/D  | [OpenID for Verifiable Credential Issuance 1.0 - ID1][OID4VCI_ID1]<sup>★★</sup>                                              |
|                    [DCP][OIDF_DCP_WG]                   |  Draft | [OpenID4VC High Assurance Interoperability Profile (HAIP) - WG Draft][HAIP_WG_DRAFT]                                         |
|                    [DCP][OIDF_DCP_WG]                   |  Draft | Security and Trust in OpenID for Verifiable Credentials                                                                      |
|                    [DCP][OIDF_DCP_WG]                   |  Draft | OpenID for Verifiable Presentations over BLE                                                                                 |
|                    [EAP][OIDF_EAP_WG]                   |  Final | [OpenID Connect Extended Authentication Profile (EAP) ACR Values 1.0][EAP_ACR_VALUES]                                        |
|                    [EAP][OIDF_EAP_WG]                   |  Draft | OpenID Connect Token Bound Authentication 1.0                                                                                |
|                [eKYC & IDA][OIDF_IDA_WG]                |  Final | [OpenID Identity Assurance Schema Definition 1.0][OPENID_IDA_SCHEMA]                                                         |
|                [eKYC & IDA][OIDF_IDA_WG]                |  Final | [OpenID Connect for Identity Assurance Claims Registration 1.0][OIDC4IDA_CLAIMS]                                             |
|                [eKYC & IDA][OIDF_IDA_WG]                |  Final | [OpenID Connect for Identity Assurance 1.0][OIDC4IDA]<sup>★★</sup>                                                           |
|                [eKYC & IDA][OIDF_IDA_WG]                |  Final | [OpenID Attachments 1.0][OPENID_ATTACHMENTS]                                                                                 |
|                [eKYC & IDA][OIDF_IDA_WG]                |  Draft | [OpenID Connect Authority claims extension][OIDC_AUTHORITY]                                                                  |
|                [eKYC & IDA][OIDF_IDA_WG]                |  Draft | [OpenID Connect Advanced Syntax for Claims (ASC) 1.0][OIDC_ASC]                                                              |
|                   [FAPI][OIDF_FAPI_WG]                  |  Final | [FAPI 2.0 Security Profile][FAPI2_SECURITY]<sup>★★★</sup>                                                                    |
|                   [FAPI][OIDF_FAPI_WG]                  |  Final | [FAPI 2.0 Attacker Model][FAPI2_ATTACKER]                                                                                    |
|                   [FAPI][OIDF_FAPI_WG]                  |  Final | [Financial-grade API Security Profile (FAPI) 1.0 – Part 1: Baseline][FAPI1_BASELINE]<sup>★</sup>                             |
|                   [FAPI][OIDF_FAPI_WG]                  |  Final | [Financial-grade API Security Profile (FAPI) 1.0 – Part 2: Advanced][FAPI1_ADVANCED]<sup>★</sup>                             |
|                   [FAPI][OIDF_FAPI_WG]                  |  Final | [JWT Secured Authorization Response Mode for OAuth 2.0 (JARM)][JARM]<sup>★★</sup>                                            |
|                   [FAPI][OIDF_FAPI_WG]                  |   I/D  | [Financial-grade API: Client Initiated Backchannel Authentication Profile][FAPI_CIBA]                                        |
|                   [FAPI][OIDF_FAPI_WG]                  |   I/D  | [Grant Management for OAuth 2.0][GRANT_MANAGEMENT]                                                                           |
|                   [FAPI][OIDF_FAPI_WG]                  |   I/D  | [FAPI 2.0 Message Signing][FAPI2_MESSAGE_SIGNING]<sup>★★</sup>                                                               |
|                   [FAPI][OIDF_FAPI_WG]                  |  Draft | [FAPI 2.0 Http Signatures][FAPI2_HTTP_SIGNATURES]<sup>★</sup>                                                                |
|                [FastFed][OIDF_FASTFED_WG]               |   I/D  | [FastFed Core 1.0 - ID1][FASTFED_CORE_ID1]                                                                                   |
|                [FastFed][OIDF_FASTFED_WG]               |   I/D  | [FastFed Enterprise SAML Profile 1.0 - ID1][FASTFED_SAML_ID1]                                                                |
|                [FastFed][OIDF_FASTFED_WG]               |   I/D  | [FastFed Enterprise SCIM Profile 1.0 - ID1][FASTFED_SCIM_ID1]                                                                |
|                  [HEART][OIDF_HEART_WG]                 |   I/D  | [Health Relationship Trust Profile for OAuth 2.0][HEART_OAUTH]                                                               |
|                  [HEART][OIDF_HEART_WG]                 |   I/D  | [Health Relationship Trust Profile for Fast Healthcare Interoperability Resources (FHIR) OAuth 2.0 Scopes][HEART_FHIR_OAUTH] |
|                  [HEART][OIDF_HEART_WG]                 |   I/D  | [Health Relationship Trust Profile for User-Managed Access 2.0][HEART_UMA]                                                   |
|                  [HEART][OIDF_HEART_WG]                 |   I/D  | [Health Relationship Trust Profile for Fast Healthcare Interoperability Resources (FHIR) UMA 2 Resources][HEART_FHIR_UMA]    |
|                   [iGov][OIDF_IGOV_WG]                  |   I/D  | [International Government Assurance Profile (iGov) for OAuth 2.0][IGOV_OAUTH]                                                |
|                   [iGov][OIDF_IGOV_WG]                  |   I/D  | [International Government Assurance Profile (iGov) for OpenID Connect][IGOV_OIDC]                                            |
|                   [iGov][OIDF_IGOV_WG]                  |  Draft | [International Government Assurance Profile (iGov) Use Cases][IGOV_USE_CASES]                                                |
|                 [MODRNA][OIDF_MODRNA_WG]                |  Final | [OpenID Connect Client-Initiated Backchannel Authentication Flow – Core 1.0][CIBA]<sup>★★</sup>                              |
|                 [MODRNA][OIDF_MODRNA_WG]                |   I/D  | [OpenID Connect MODRNA Authentication Profile 1.0][MODRNA_AUTH]                                                              |
|                 [MODRNA][OIDF_MODRNA_WG]                |   I/D  | [OpenID Connect Account Porting][ACCOUNT_PORTING]                                                                            |
|                 [MODRNA][OIDF_MODRNA_WG]                |   I/D  | [OpenID Connect User Questioning API 1.0][USER_QUESTIONING]                                                                  |
|                 [MODRNA][OIDF_MODRNA_WG]                |  Draft | [OpenID Connect MODRNA Discovery Profile 1.0][MODRNA_DISCOVERY]                                                              |
|                 [MODRNA][OIDF_MODRNA_WG]                |  Draft | [OpenID Connect Mobile Registration Profile 1.0][MOBILE_REGISTRATION]                                                        |
|               [Shared Signals][OIDF_SS_WG]              |   I/D  | [OpenID Shared Signals Framework Specification 1.0][SSF]<sup>★★</sup>                                                        |
|               [Shared Signals][OIDF_SS_WG]              |   I/D  | [OpenID Continuous Access Evaluation Profile 1.0][CAEP]                                                                      |
|               [Shared Signals][OIDF_SS_WG]              |   I/D  | [OpenID RISC Profile Specification 1.0][RISC]                                                                                |
|                                                         |        |                                                                                                                              |
| I/D = Implementer's Draft (実装者向けドラフト)<br />2025 年 7 月現在 |        |                                                                                                                              |
|                                                         |        |                                                                                                                              |

これらに加え、他所で作成された標準仕様も OAuth や OpenID の文脈で参照されます。
例えば、[OpenID Shared Signals Framework Specification 1.0][SSF] は、IETF の
[SECEVENT ワーキンググループ][IETF_SECEVENT_WG] (解散済み) が策定した次の
RFC 群に依存しています。

|    日付   |          RFC         | タイトル                                                                  |
| :-----: | :------------------: | :-------------------------------------------------------------------- |
| 2018-07 | [RFC 8417][RFC_8417] | [Security Event Token (SET)][RFC_8417]                                |
| 2020-11 | [RFC 8935][RFC_8935] | [Push-Based Security Event Token (SET) Delivery Using HTTP][RFC_8935] |
| 2020-11 | [RFC 8936][RFC_8936] | [Poll-Based Security Event Token (SET) Delivery Using HTTP][RFC_8936] |
| 2023-12 | [RFC 9493][RFC_9493] | [Subject Identifiers for Security Event Tokens][RFC_9493]             |

同様に、[FAPI 2.0 Http Signatures][FAPI2_HTTP_SIGNATURES] は、IETF の
[HTTPBIS ワーキンググループ][IETF_HTTPBIS_WG]が策定した次の RFC 群に依存しています。

|    日付   |          RFC         | タイトル                                                     |
| :-----: | :------------------: | :------------------------------------------------------- |
| 2021-02 | [RFC 8941][RFC_8941] | [Structured Field Values for HTTP][RFC_8941]<sup>★</sup> |
| 2024-02 | [RFC 9421][RFC_9421] | [HTTP Message Signatures][RFC_9421]<sup>★</sup>          |
| 2024-02 | [RFC 9530][RFC_9530] | [Digest Fields][RFC_9530]                                |

ご懸念される通り、依存関係に言及し始めると際限がありません。
なぜなら、ほぼ全ての場合において、新しい仕様は既存の仕様群を基盤として作成されるからです。
結果として、一般的には、新しい仕様ほどそれを理解するために要求される前提知識の量が多くなります。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/standards_dependencies.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=42178c065e5de0b05063734067ddb620" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/standards_dependencies.png" />

この状況の問題点は、たとえ API 保護のための新しい標準仕様が公開されても、その内容を調査・検討するためのハードルが高いせいで、新しい仕様の利活用が進まないことです。
ひいては、年月の経過とともに、積極的に更新されないシステムの API セキュリティは相対的に下がっていってしまいます。
攻撃側が狡猾に進化していくことを考慮すると、この問題は深刻と言えます。

そこで、本記事では、[RFC 6749][RFC_6749] 以降に開発された標準仕様をフル活用して
API を保護する方法を紹介していこうと思います。アクセストークンの使い方 ([RFC 6750][RFC_6750])
と情報取得 ([RFC 7662][RFC_7662]) という基本から始め、`resource` パラメータ
([RFC 8707][RFC_8707]) による受信者限定、MTLS ([RFC 8705][RFC_8705]) や
DPoP ([RFC 9449][RFC_9449]) による送信者限定、HTTP メッセージ署名
([RFC 9421][RFC_9421])、[Cedar][CEDAR]、といったトピックを扱います。

その他、我々 (Authlete 社) が常にそうであるように、開発者の皆様に寄り添うため、この記事では実装者視点の事柄も扱います。例えば次のようなトピックを扱います。

* システム間の時刻ずれを考慮したアクセストークン有効期限切れの判定方法
* リバースプロキシ・クライアント間の相互 TLS 接続で用いられたクライアント証明書を受け取る方法
* 派生コンポーネント `@target-uri` ([RFC 9421][RFC_9421]) をリバースプロキシの後ろで計算する方法
* HTTP メッセージ署名の検証に使う公開鍵を取得する方法

それでは、始めましょう！

## アクセストークンの使い方

[RFC 6750][RFC_6750] (Bearer Token Usage) では、アクセストークンをリソースサーバに提示する方法として次の三つが示されています。

1. `Authorization` ヘッダを用いる方法 ([Section 2.1][RFC_6750_2_1])
2. フォームパラメータを用いる方法 ([Section 2.2][RFC_6750_2_2])
3. クエリパラメータを用いる方法 ([Section 2.3][RFC_6750_2_3])

しかしながら、二番目の方法は HTTP リクエストのメッセージボディのフォーマットが
`application/x-www-form-urlencoded`
に限定されてしまうこと、三番目の方法はセキュリティ上の問題があることから、一番目の方法が用いられることがほとんどです。

`Authorization` ヘッダを用いる方法では、ヘッダの値を

```text theme={null}

Bearer アクセストークン

```

という形式にします。`Bearer` 部分は固定文字列であり、*アクセストークン*の箇所は実際のアクセストークンの値で置き換えます。

下記は、アクセストークン
`i_1X-euOC6-45zdObsQty7hgDMW9RUTjSGb0pzP69X0`
を使って `https://rs.example.com/resource/1` にアクセスする例です。

```text theme={null}

GET https://rs.example.com/resource/1

Authorization: Bearer i_1X-euOC6-45zdObsQty7hgDMW9RUTjSGb0pzP69X0

```

一方、アクセストークンを DPoP ([RFC 9449][RFC_9449]) という仕組みを用いてよりセキュアにしている場合、`Bearer`
という固定文字列を `DPoP` に変更します。また、`DPoP` ヘッダも追加します。
`DPoP` ヘッダの値は DPoP Proof JWT と呼ばれる JWT ([RFC 7519][RFC_7519]) の一種です。

```text theme={null}

Authorization: DPoP アクセストークン

DPoP: DPoP Proof JWT

```

下記は、DPoP 対応アクセストークンを用いてリソースにアクセスする例です。

```text theme={null}

GET https://rs.example.com/resource/1

Authorization: DPoP i_1X-euOC6-45zdObsQty7hgDMW9RUTjSGb0pzP69X0

DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwiYWxnIjoiRVMyNTYiLCJjcnYiOiJQLTI1NiIsIngiOiIxQW1WcjRHb0hkUGdrNDhMV2RTM1Q5bTZtMW1QNFZUY2o5dXNvU0JuQ1FrIiwieSI6InRlLVdJdVVJcTJ3OHRYbVh5ZGxFWDRwZTlsTmUtUEJjb3pBOG43eThYVEUifX0.eyJqdGkiOiJvY1A3ZVc0d3l1bG0yWmMzIiwiaHRtIjoiR0VUIiwiaHR1IjoiaHR0cHM6Ly9ycy5leGFtcGxlLmNvbS9yZXNvdXJjZS8xIiwiaWF0IjoxNzU0MDMxMzU1LCJhdGgiOiJFWVlRSk9vWDlYLUt6TVBBNTI3NnBjelR0cHpVMjhMRzltQWRVeXdIa2dVIn0.Jg8sTKhaZvPJY0p--NaFEHJhkuM0SN4CHUbPe5xaxuZBLHZNfTtRJjpSb_6P-utmNYqQRuQ1rEPUq8ivKPP1MQ

```

DPoP の詳細は後述します。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/access_token_usage.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=20174aaba3392dad24dbdcf80b24437b" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/access_token_usage.png" />

## アクセストークン情報取得

提示されたアクセストークンの有効性を確認するため、リソースサーバはまず、アクセストークンの情報を取得する必要があります。

情報がアクセストークン自体に埋め込まれている場合、アクセストークンの内容を読むことで情報を取得できます。
一方、情報が埋め込まれていない場合は、認可サーバのイントロスペクションエンドポイント
([RFC 7662][RFC_7662]) に問い合わせをして情報を取得します。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/access_token_information_retrieval.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=1d85b01f325a80770bda7bd9e414b6a1" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/access_token_information_retrieval.png" />

一見、アクセストークンに情報が埋め込まれている形式の方がリソースサーバの負担は少ないように見えますが、この形式の場合、アクセストークンの内容が改竄されていないことを確認する手間が別途かかります。

情報をアクセストークン自体に埋め込む場合、改竄検出が可能なことから、アクセストークンの形式として
JWT ([RFC 7519][RFC_7519]) を用いることがほとんどです。JWT 発行者
(この文脈ではアクセストークンを発行した認可サーバ) の公開鍵を用いて JWT の署名を検証することで、その
JWT の内容が改竄されていないことを確認できます。

### JWT アクセストークンの署名検証

ここで、JWT 形式のアクセストークンの署名を検証する手順を見ていきましょう。

まず、JWT のペイロード部にある `iss` クレーム ([RFC 7519 Section 4.1.1][RFC_7519_4_1_1])
の値を読み取ります。このクレームは JWT 発行者の識別子を表しています。
アクセストークンを発行するのは、認可サーバ、または認可サーバを兼ねる OpenID
プロバイダなので、JWT アクセストークンの `iss` の値は、認可サーバまたは
OpenID プロバイダの識別子を表していることになります。

認可サーバと OpenID プロバイダの識別子は、関連仕様 ([RFC 8414][RFC_8414],
[OIDC Core][OIDC_CORE], [OIDC Discovery][OIDC_DISCOVERY]) により `https` で始まる
URL と定められています。結果として、`iss` クレームの値は、アクセストークンを発行した認可サーバまたは
OpenID プロバイダ (HTTP サーバ) の URL を表しています。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/jwt_access_token_signature_verification-0.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=0acc08de6dc825d3ccfa273cb9863fa5" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/jwt_access_token_signature_verification-0.png" />

アクセストークンの発行者識別子が得られれば、その情報を元に発行者のメタデータが公開されている場所を求めることができます。

認可サーバが [RFC 8414][RFC_8414] (OAuth 2.0 Authorization Server Metadata)
をサポートしていれば、発行者識別子に
`/.well-known/oauth-authorization-server`
を追加した場所でメタデータが公開されています。
この場所からサーバメタデータを JSON 形式で取得できます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/jwt_access_token_signature_verification-1.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=288f61feaf52b9d4d8bd5a91bb3c6ab2" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/jwt_access_token_signature_verification-1.png" />

<Note>
  ただし、発行者識別子がパス部を含む場合は、ホスト部とパス部の間に
  `/.well-known/oauth-authorization-server`
  を追加した場所でメタデータが公開されています。
  詳細は [RFC 8414](https://www.rfc-editor.org/rfc/rfc8414.html)
  の [Section
  3.1. Authorization Server Metadata Request](https://www.rfc-editor.org/rfc/rfc8414.html#section-3.1) を参照してください。
</Note>

または、サーバが [OpenID Connect Discovery 1.0][OIDC_DISCOVERY]
をサポートしていれば、発行者識別子に
`/.well-known/openid-configuration`
を追加した場所でメタデータが公開されています。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/jwt_access_token_signature_verification-2.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=8ffe948d82b987f7400bbdd23b3ba9a8" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/jwt_access_token_signature_verification-2.png" />

サーバメタデータには多くの情報が含まれていますが、ここで重要なのは `jwks_uri`
メタデータです。これは、サーバの JWK セットドキュメントが公開されている場所を示しています。
この場所から JWK セットドキュメントをダウンロードすることができます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/jwt_access_token_signature_verification-3.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=9b8287413fb634076a3d900fa87223ca" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/jwt_access_token_signature_verification-3.png" />

JWK セットドキュメントの内容は [RFC 7517][RFC_7517] の
[Section 5. JWK Set Format][RFC_7517_5] で定義されているフォーマットに従っています。
そのフォーマットは JSON オブジェクトであり、`keys` というトップレベルプロパティを一つ含んでいます。
その `keys` プロパティの値は、JWK ([RFC 7517][RFC_7517]) の JSON 配列です。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/jwt_access_token_signature_verification-4.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=776b33de120431de468a1696a5730587" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/jwt_access_token_signature_verification-4.png" />

その JWK 群の中に、JWT アクセストークンの署名の検証のための公開鍵が含まれているはずです。
JWT の JWS ヘッダが `kid` パラメータ ([RFC 7515 Section 4.1.4][RFC_7515_4_1_4])
を含んでいれば、その値と同じ `kid` ([RFC 7517 Section 4.5][RFC_7517_4_5])
を持つ JWK を探すことで、検証鍵を特定することができます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/jwt_access_token_signature_verification-5.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=57f4e779c764ac2bbe5bc2f6155faf58" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/jwt_access_token_signature_verification-5.png" />

JWT が `kid` を含んでいない場合は、検証鍵の特定方法は実装固有の方法になるでしょう。
例えば、`alg` ([RFC 7515 Section 4.1.1][RFC_7515_4_1_1],
[RFC 7517 Section 4.4][RFC_7517_4_4]) や `use`
([RFC 7517 Section 4.2][RFC_7517_4_2])
などで条件を絞り込んで検証鍵を特定する、といった方法が考えられます。

検証鍵が特定できたら、その鍵を用いて JWT の署名を検証します。
検証がパスすれば、JWT アクセストークンが改竄されていないと言えます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/jwt_access_token_signature_verification-6.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=7506002cee88b69bf147862fed5b0b62" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/jwt_access_token_signature_verification-6.png" />

まとめ

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/jwt_access_token_signature_verification.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=dc033867d472623ef6d9018d7bfe1ed2" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/jwt_access_token_signature_verification.png" />

## 時刻

アクセストークンは、最大で次の三つの時刻関連属性を持ちえます。

1. 発行時刻
2. 有効期限開始時刻
3. 有効期限終了時刻

アクセストークンの情報をイントロスペクションエンドポイントから得たのか、または
JWT のペイロードから得たのか、に関わらず、これらの値は `iat` (Issued At)、`nbf`
(Not Before)、`exp` (Expiration Time) という名前で参照されます。

|          |   名前  |            イントロスペクションレスポンス           |                 JWT ペイロード                |
| :------- | :---: | :----------------------------------: | :--------------------------------------: |
| 発行時刻     | `iat` | [RFC 7662 Section 2.2][RFC_7662_2_2] | [RFC 7519 Section 4.1.6][RFC_7519_4_1_6] |
| 有効期間開始時刻 | `nbf` | [RFC 7662 Section 2.2][RFC_7662_2_2] | [RFC 7519 Section 4.1.5][RFC_7519_4_1_5] |
| 有効期間終了時刻 | `exp` | [RFC 7662 Section 2.2][RFC_7662_2_2] | [RFC 7519 Section 4.1.4][RFC_7519_4_1_4] |

これらの属性の値により、アクセストークンの有効期間が決まります。
次の図は、`iat`、`nbf`、`exp` が全て指定されている場合のアクセストークン有効期間を示したものです。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/access_token_validity_period.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=34acb5e9aae7963e30d503eb33d19606" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/access_token_validity_period.png" />

リソースサーバは、現在時刻がアクセストークンの有効期間内であることを確認します。
具体的には次の確認を行います。

1. 発行時刻が現在時刻以前である
2. 有効期間開始時刻が現在時刻以前である
3. 有効期間終了時刻が現在時刻より後である

ただし、実際に運用するシステムでは、システム間の時刻ずれの可能性を考慮して確認を行います。
というのは、本来有効であると判定されるべきアクセストークンが、時刻ずれの影響で無効であると判定されることが、現実世界ではよく起こるからです。
例えば、アクセストークンを受け取るシステム (リソースサーバ) の時刻が、アクセストークンを発行したシステム
(認可サーバ) の時刻よりも遅れていると、「アクセストークンの有効期間がまだ始まっていない
(現在時刻が `nbf` で指定された時刻より前である)」と判定される可能性があります。

*「NTP (Network Time Protocol) を使って時刻同期をしていればそういう問題は起こらないはずだ」*
という意見も耳にすることもあるでしょうが、現実的には起こります。
各国のオープンバンキングエコシステムの長年の運用経験から得られた知見が [FAPI 2.0 Security Profile][FAPI2_SECURITY]
に書かれているので、ここでご紹介します。

> NOTE 3: Clock skew is a cause of many interoperability issues. Even a few
> hundred milliseconds of clock skew can cause JWTs to be rejected for being
> "issued in the future". The DPoP specification \[[RFC9449][RFC_9449]]
> suggests that JWTs are accepted in the reasonably near future (on the order
> of seconds or minutes). This document goes further by requiring authorization
> servers to accept JWTs that have timestamps up to 10 seconds in the future.
> 10 seconds was chosen as a value that does not affect security while greatly
> increasing interoperability. Implementers are free to accept JWTs with a
> timestamp of up to 60 seconds in the future. Some ecosystems have found that
> the value of 30 seconds is needed to fully eliminate clock skew issues. To
> prevent implementations switching off `iat` and `nbf` checks completely this
> document imposes a maximum timestamp in the future of 60 seconds.
>
> 注3： クロックスキュー (時刻のずれ) は、多くの相互運用性の問題の原因となっています。
> 数百ミリ秒程度のわずかな時刻ずれであっても、JWT が「未来に発行された」と判断されて拒否される可能性があります。
> DPoP 仕様 (\[[RFC9449][RFC_9449]]) では、数秒から数分といった「現実的に近い未来」であれば
> JWT を受け入れるよう提案されています。本ドキュメントではさらに踏み込んで、認可サーバは最大で
> 10 秒未来のタイムスタンプを持つ JWT を受け入れることを必須としています。
> この「10 秒」という値は、セキュリティに影響を与えずに相互運用性を大きく向上させる値として選ばれました。
> 実装者は、最大で 60 秒未来のタイムスタンプを持つ JWT を受け入れても構いません。
> 一部のエコシステムでは、クロックスキュー問題を完全に解消するためには 30 秒が必要であるとされています。
> なお、本ドキュメントでは、実装が `iat` (発行時刻) や `nbf` (有効期間開始時刻)
> のチェックを完全に無効化することを防ぐために、未来時刻の上限を最大 60 秒とする制限を設けています。

技術的には、時刻ずれの考慮には、判定を厳しくする方向 (有効と判定されにくくなる)
と、判定を緩くする方向 (有効と判定されやすくなる) があります。
システム運用の現場で求められているのは後者です。
これを踏まえ、`iat` と `nbf` を現在時刻と比較する際は許容する最大時刻ずれ
(例えば 10 秒) を引いてから比較をおこない、`exp`
を現在時刻と比較する際は許容する最大時刻ずれを足してから比較をおこないます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/access_token_validity_period_with_clock_skew.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=a25a34457591d809f4592bceb033b3c0" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/access_token_validity_period_with_clock_skew.png" />

## スコープ

RFC 6749 が定義するアクセストークンの基本的な属性の一つとして、スコープがあります
([RFC 6749 Section 3.3][RFC_6749_3_3])。
スコープはアクセストークンが持つ権限群を表しています。

API 群は、それぞれの目的に応じ、アクセストークンが特定のスコープを持つことを要求することがあります。
どのようなスコープを要求するかは、API 提供者が任意に定めます。
API の実装は、処理を先に進める前に、提示されたアクセストークンが必要なスコープを持っているかどうかを確認します。

<Note>
  ここでは API と呼んでいますが、保護リソースやエンドポイントなど、別の呼称も用いられます。
  呼称の使い分けに明確な基準はありません。
</Note>

クライアントアプリケーションは自分が利用する予定の API
が要求するスコープを予め調べておき、アクセストークンの発行を認可サーバに依頼する際にそのスコープを依頼内容の一部として含めます。
具体的には、たとえば認可コードフロー ([RFC 6749 Section 4.1][RFC_6749_4_1])
を用いてアクセストークンの発行を依頼する場合、認可リクエスト
([RFC 6749 Section 4.1.1][RFC_6749_4_1_1]) の `scope`
リクエストパラメータの値として、個々のスコープ名をスペース区切りで列挙します。

具体的な例を見てみましょう。

クライアントアプリケーションが、あるサーバが公開している `GET /ssf/status` API と
`POST /ssf/status` API を利用したいとします。そして、`GET /ssf/status` API は
`ssf:status:read` スコープを、`POST ssf/status` API は
`ssf:status:update` スコープを要求するとします。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/access_token_scope-0.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=8319a5de7633b0bd9b15b1b9a9dfed72" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/access_token_scope-0.png" />

クライアントアプリケーションは、それらのスコープ名を `scope`
リクエストパラメータの値に含む認可リクエストを認可サーバに送ります。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/access_token_scope-1.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=e60dafb17f115ab8a87cd212f3902a89" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/access_token_scope-1.png" />

認可サーバは、ユーザの承認を得た後、それらのスコープ群が紐付いたアクセストークンをクライアントアプリケーションに発行します。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/access_token_scope-2.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=e92780c679fef619c8e9ea43e3d17991" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/access_token_scope-2.png" />

クライアントアプリケーションは、アクセストークンを添えて API にアクセスします。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/access_token_scope-3.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=6eec7f5565a53e0dbfcba22afbc13e9a" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/access_token_scope-3.png" />

まとめ

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/access_token_scope.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=5db2c21ca48113ae10d154eca3cb0962" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/access_token_scope.png" />

## 受信者限定

アクセストークンの受信者 (audience) を制限する方法があります。

その方法を用いることにより、例えば、ある特定のリソースサーバだけを受信者と指定したり、特定のパス階層下にあるエンドポイント群のみを受信者として指定したりすることができます。
これにより、アクセストークンの誤用や悪用の可能性を減らすことができます。

アクセストークンの受信者は、イントロスペクションレスポンス ([RFC 7662][RFC_7662]) と
JWT アクセストークンの双方とも、`aud` パラメータで表現されます。

|     |   名前  |            イントロスペクションレスポンス           |                 JWT ペイロード                |
| :-- | :---: | :----------------------------------: | :--------------------------------------: |
| 受信者 | `aud` | [RFC 7662 Section 2.2][RFC_7662_2_2] | [RFC 7519 Section 4.1.3][RFC_7519_4_1_3] |

例えば、イントロスペクションレスポンスが次のように `aud` プロパティの値に
`https://rs.example.com` を含んでいれば、そのアクセストークンは
`https://rs.example.com` でのみ使えることを意味します。

```json theme={null}
{
  "aud": [
    "https://rs.example.com"
  ]
}
```

仮に、そのアクセストークンを `https://rs.example.org`
に提示しても、`https://rs.example.org` 上の API
の実装群がアクセストークンの `aud`
プロパティの値を正しくチェックしていれば、そのアクセストークンは拒否されます。

アクセストークンの受信者の制限は、[RFC 8707][RFC_8707]
(Resource Indicators for OAuth 2.0) で定義されている `resource`
リクエストパラメータ ([RFC 8707 Section 2][RFC_8707_2]) を用いておこないます。

認可リクエストやトークンリクエストに `resource` リクエストパラメータを含めると、その値が
アクセストークンの受信者として設定されます。下記は [RFC 8707][RFC_8707] の
[Section 2.1][RFC_8707_2_1] から抜粋した `resource` リクエストパラメータの使用例です。
この例では、`https://cal.example.com` と
`https://contacts.example.com`
の二つがアクセストークン受信者として指定されています。

```text theme={null}

GET /as/authorization.oauth2?response_type=code

   &client_id=s6BhdRkqt3

   &state=tNwzQ87pC6llebpmac_IDeeq-mCR2wLDYljHUZUAWuI

   &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb

   &scope=calendar%20contacts

   &resource=https%3A%2F%2Fcal.example.com%2F

   &resource=https%3A%2F%2Fcontacts.example.com%2F HTTP/1.1

Host: authorization-server.example.com

```

<Note>
  [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html) の
  [Section 3.1](https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1)
  には\*「リクエストパラメータおよびレスポンスパラメータは複数回含めてはなりません。」\*
  *(Request and response parameters MUST NOT be included more than once.)*
  と書かれていますが、[RFC 8707](https://www.rfc-editor.org/rfc/rfc8708.html)
  で定義される `resource` リクエストパラメータはこの制約を受けません。
</Note>

このように受信者が制限されたアクセストークンを [RFC 8707][RFC_8707] では「受信者限定
(audience-restricted)」アクセストークンと呼んでいます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/audience_restricted_access_token.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=d4335da3e440520e8b9534677e641623" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/audience_restricted_access_token.png" />

## 送信者限定

従来のアクセストークンは、漏洩してしまうと、攻撃者がそれを用いて API にアクセスすることができてしまいます。
これは、電車の切符を失くしてしまったら、それを拾った他の人が電車に乗れてしまう問題と同じです。

この脆弱性を軽減する手段として、アクセストークンの発行対象者とアクセストークンの利用者が同一であることを
API アクセス時にチェックするという方法が考えられます。
これは、国際線航空チケットの利用時に、チケットと併せてパスポートの提示も要求し、正規のチケット利用者とチケット持参者が同一であることを確認する手続きと同じです。

アクセストークンの正当な所有者であることを示すものを **Proof of Possession** (**PoP**) と呼びます。
リソースサーバがクライアントアプリケーションに対し、アクセストークンと併せて PoP
の提示を要求することにより、アクセストークンだけを窃取しても API を不正利用できなくなります。

PoP を要求することにより、結果として、アクセストークンを送ってくる者 (sender) を制限することになります。
このため、利用時に PoP の提示も要求されるアクセストークンを「送信者限定
(sender-constrained)」アクセストークンと呼びます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/sender_constrained_access_token.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=c088b5afd58e2489e37c73b79813c87a" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/sender_constrained_access_token.png" />

送信者限定アクセストークンを実現する方法に MTLS ([RFC 8705 Section 3][RFC_8705_3]) と
DPoP ([RFC 9449][RFC_9449]) があります。次の二節でこれらについて説明します。

## MTLS

[RFC 8705][RFC_8705] の [Section 3][RFC_8705_3] では、クライアントアプリケーションの
X.509 証明書 ([RFC 5280][RFC_5280]) (以降、クライアント証明書) を利用して PoP
を実現する方法 (以降 MTLS) を定めています。

<Note>
  X.509 証明書については『[図解
  X.509 証明書](https://qiita.com/TakahikoKawasaki/items/4c35ac38c52978805c69)』の解説をご参照ください。
</Note>

### 相互 TLS 接続

MTLS を用いる場合、クライアントアプリケーションと認可サーバのトークンエンドポイント間の
TLS 接続、および、クライアントアプリケーションとリソースサーバの API 間の TLS 接続を、相互
TLS 接続とすることが前提となります。また、それらの TLS
接続で、クライアントアプリケーションが同一の証明書を提示することが求められます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/mutual_tls_connection.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=b3371569b0abc0a9d586865b0496da03" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/mutual_tls_connection.png" />

相互 TLS 接続とは、通信の両者 (クライアントとサーバ) が互いの正当性を証明し合う TLS 接続のことです。
通常の TLS 接続では、サーバが自分の証明書を提示し、クライアントがその証明書を検証するだけです。
しかし、相互 TLS 接続ではクライアントも証明書を提示し、サーバもクライアントの証明書を検証します。

<Note>
  プロトコルの定義上、TLS 接続を確立する際にサーバから求められない限り、クライアントはクライアント証明書を提示しません。
  つまり、クライアント側から相互 TLS 接続を要求することはできません。そのため、相互 TLS
  接続のためには、サーバ側の設定で相互 TLS 接続を有効にする必要があります。
</Note>

### 証明書バインディング

MTLS の基本的なアイディアは、「トークンリクエスト時の相互 TLS
接続で用いられたクライアント証明書」を生成するアクセストークンに紐付けて覚えておき、
その後のリソースリクエスト時、「リソースリクエスト時の相互 TLS 接続で用いられたクライアント証明書」がリソースリクエストに含まれるアクセストークンに紐付いているものと同一であるか確認する、というものです。

同一であれば、トークンリクエストをおこなったクライアントアプリケーションとリソースリクエストをおこなっているクライアントアプリケーションが同一であるとみなします。同一でなければ、リソースリクエストをおこなっているクライアントアプリケーションはアクセストークンの正当な保有者ではないと判断し、リソースリクエストを拒否します。

それでは、MTLS により送信者限定アクセストークンが実現される手順を見ていきましょう。

クライアントアプリケーションは、認可サーバのトークンエンドポイントと相互 TLS
接続を確立し、トークンリクエストを送信します。
相互 TLS 接続なので、クライアントはクライアント証明書をサーバに提示します。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/certificate_bound_access_token-0.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=94f7e88b8a5cab665d81a0b262751b68" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/certificate_bound_access_token-0.png" />

トークンエンドポイントの実装では、トークンリクエストが有効であればアクセスストークンを生成し、それをデータベースに保存します。
ただし、アクセストークンが情報を自分自身に埋め込む形式の場合 (JWT アクセストークンなど)、データベースにデータを書き込まない実装もありえます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/certificate_bound_access_token-1.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=ded0d06b53c8a33969b640430ca84a36" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/certificate_bound_access_token-1.png" />

このとき、相互 TLS 接続からクライアント証明書を取り出し、そのハッシュ値を計算し、アクセストークンと紐付けて覚えておきます。
ここでハッシュ値は、DER エンコード ([X.690][X_690]) されたクライアント証明書の
SHA-256 ハッシュ ([NIST FIPS 180-4][NIST_FIPS_180_4]) です。

このように、証明書と紐付けられたアクセストークンのことを「<ruby>certificate<rp> (</rp><rt>サーティフィケート</rt><rp>)
</rp></ruby>-<ruby>bound<rp> (</rp><rt>バウンド</rt><rp>)
</rp></ruby>」アクセストークンと呼びます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/certificate_bound_access_token-2.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=2bde481c887c79475abf7b5d2ba3a35b" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/certificate_bound_access_token-2.png" />

トークンエンドポイントは、生成したアクセストークンを含むトークンレスポンスをクライアントアプリケーションに返します。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/certificate_bound_access_token-3.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=7e0f9f4c2707ebd8d84d2d55790607f9" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/certificate_bound_access_token-3.png" />

アクセストークン取得後、クライアントアプリケーションはリソースサーバの API との間に相互
TLS 接続を確立します。このとき、トークンリクエストの際に用いたのと同じクライアント証明書を使います。

[<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/certificate_bound_access_token-4.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=4a1a3f17ae9e1090111e98c7d40ad8d7" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/certificate_bound_access_token-4.png" />](/img/kb/ja/api_protection/certificate_bound_access_token.png)

接続確立後、アクセストークンを添えてリソースリクエストを送信します。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/certificate_bound_access_token-5.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=8b8a2e4b1d89e3ba9ab4d64da5141bf0" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/certificate_bound_access_token-5.png" />

API の実装はリクエストからアクセストークンを取り出し、そのアクセストークンを添えて認可サーバのイントロスペクションエンドポイントに問い合わせをおこないます。
ただし、アクセストークンの情報がアクセストークン自身に含まれている場合は、イントロスペクションエンドポイントに問い合わせるかわりにアクセストークンの内容を読みます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/certificate_bound_access_token-6.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=4ec6aeb9609025db86945591a6c42cf9" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/certificate_bound_access_token-6.png" />

イントロスペクションエンドポイントの実装は、提示されたアクセストークンの情報をデータベースから取り出し、イントロスペクションレスポンスに整形してリソースサーバに返します。
この際、アクセストークンに紐付くクライアント証明書のハッシュ値を base64url
([RFC 4648][RFC_4648]) エンコードし、`cnf` というトッププロパティ内の
`x5t#S256` サブプロパティの値としてイントロスペクションレスポンスに埋め込みます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/certificate_bound_access_token-7.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=08ddcb62df88892cea6739900c9120a9" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/certificate_bound_access_token-7.png" />

<Note>
  `x5t#S256` プロパティを定義しているのは
  [RFC 8705](https://www.rfc-editor.org/rfc/rfc8705.html)
  ですが、`cnf` プロパティを定義しているのは
  [RFC 7800](https://www.rfc-editor.org/rfc/rfc7800.html)
  (Proof-of-Possession Key Semantics for JSON Web Tokens (JWTs)) です。
</Note>

次に、API の実装は、クライアントアプリケーションとの相互 TLS 接続からクライアント証明書を取り出し、そのハッシュ値を計算します。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/certificate_bound_access_token-8.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=fd3c1ac7baac0dd80bd545c4d8c9dc30" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/certificate_bound_access_token-8.png" />

そして、計算されたハッシュ値とイントロスペクションレスポンスに含まれるハッシュ値が一致するかどうかを確認します。
一致しなければ、リソースリクエストをおこなっているクライアントアプリケーションはアクセストークンの正当な保有者ではないと判断し、リソースリクエストを拒否します。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/certificate_bound_access_token-9.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=bb6499e635b94c31566a5fe40860ac95" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/certificate_bound_access_token-9.png" />

まとめ

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/certificate_bound_access_token.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=9a0a3aefa63ea1077e266c74c26ceb0c" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/certificate_bound_access_token.png" />

### クライアント証明書の転送

TLS で保護されたアプリケーションサーバを配備する際、クライアントとアプリケーションサーバの間にリバースプロキシを置くという構成は一般的です。
リバースプロキシは、クライアントとの TLS 接続を担当し、そこで TLS
接続を終端させてから、後方に控えるアプリケーションサーバにクライアントからのリクエストを転送します。

このような構成では、アプリケーションサーバはクライアントと直接 TLS
接続をおこないません。そのため、たとえクライアントがリバースプロキシとの間に相互
TLS 接続を確立していたとしても、その接続内でクライアントが提示したクライアント証明書をアプリケーションサーバは参照できません。
もしもアプリケーションサーバのロジックでクライアント証明書を参照したければ (例えば certificate-bound
アクセストークンを生成したり検証したりしたければ)、何らかの方法でリバースプロキシからクライアント証明書を転送してもらわなければなりません。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/client_certificate_forwarding-0.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=87bcf0ed020d08aebed74a7b7223a369" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/client_certificate_forwarding-0.png" />

この目的のため、「クライアント証明書を値として持つカスタム HTTP ヘッダ
(例: `X-Ssl-Cert`) を転送するリクエストに追加する」という方法が昔からよく行われてきました。
[RFC 9440][RFC_9440] は、この HTTP ヘッダの名前として `Client-Cert` を定義し、標準化しました。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/client_certificate_forwarding-1.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=2c2f64851851236069f0ef23050821c0" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/client_certificate_forwarding-1.png" />

[RFC 9440][RFC_9440] は、クライアント証明書を HTTP ヘッダの値として埋め込む際のフォーマットも標準化しました。
それまでは、PEM フォーマット ([RFC 7468][RFC_7468])
を使っている実装がよく見られましたが、改行の有無や `BEGIN` / `END`
バウンダリーの有無などの揺れがあり、互換性は低い状態でした。
[RFC 9440][RFC_9440] は、フォーマットを「DER エンコード ([X.690][X_690])
されたクライアント証明書をあらわすバイトシーケンス
([RFC 8941 Section 3.3.5][RFC_8941_3_3_5])」と定めました。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/client_certificate_forwarding-2.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=1f7f9f2a7e0aa36993eac49ff8c4b808" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/client_certificate_forwarding-2.png" />

<Note>
  [RFC 8941](https://www.rfc-editor.org/rfc/rfc8941.html)
  の[バイトシーケンス](https://www.rfc-editor.org/rfc/rfc8941.html#section-3.3.5)のフォーマットを簡単に説明すると、「`:{base64}:`」となります。
  元データを Base64 ([RFC 4648](https://www.rfc-editor.org/rfc/rfc4648.html))
  でエンコードし、両脇にコロン (`:`) を置きます。
</Note>

まとめ

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/client_certificate_forwarding.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=e7246f7e93e4ea4f9d0f05de9bdc5f3c" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/client_certificate_forwarding.png" />

## DPoP

[RFC 9449][RFC_9449]、通称 <ruby>DPoP<rp> (</rp><rt>ディーポップ</rt><rp>) </rp></ruby>
は、送信者限定アクセストークンを実現する方法の一つです。

DPoP では、アクセストークン生成時、クライアントアプリケーションが用意した鍵ペアの公開鍵の方をアクセストークンと紐付けておきます。
そして、アクセストークン利用時にアクセストークンと併せて「アクセストークンに紐付いている公開鍵とペアとなっている秘密鍵を持っていること示す証拠」も要求することで、送信者限定を実現します。

その証拠は、公開鍵を埋め込んだデータに秘密鍵で署名することで生成します。いわゆる自己署名トークンです。

自己署名トークン受信者は、自己署名トークンに埋め込まれた公開鍵を取り出し、その公開鍵で自己署名トークンの署名を検証します。
検証がパスした場合、そのトークンを生成した者はペアとなっている秘密鍵を持っていると言えます。

それでは、DPoP の細かい手順を見ていきましょう。

### DPoP バインディング

クライアントアプリケーションは、まず、鍵ペアを作成します。
作成した鍵ペアの公開鍵は、以降の処理で生成されるアクセストークンに紐付けられることになります。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/dpop_bound_access_token-00.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=fae6f267e812bed1bda3bbff55631d7c" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/dpop_bound_access_token-00.png" />

次に、PoP を作成します。[RFC 9449][RFC_9449] で定義される PoP は **DPoP proof JWT**
と呼ばれ、仕様が細かく決められています。要点は次の通りです。

* フォーマットは JWT
* `typ` ヘッダパラメータの値は `dpop+jwt`
* `jwk` ヘッダパラメータにアクセストークンに紐付ける公開鍵をセットする
* `htm` クレームの値は、送信予定のリクエストの HTTP メソッド
* `htu` クレームの値は、送信予定のリクエストのターゲット URI (ただしクエリパラメータ、フラグメントパラメータを除く)
* `jti` クレーム、`iat` クレームも必須
* `jwk` ヘッダパラメータに指定した公開鍵とペアになっている秘密鍵で署名する

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/dpop_bound_access_token-01.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=c75b8f36131bd78711df94963be5c046" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/dpop_bound_access_token-01.png" />

認可サーバのトークンエンドポイントにトークンリクエストを送ります。
この際、事前に作成しておいた DPoP proof JWT を `DPoP`
ヘッダの値としてリクエストに含めます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/dpop_bound_access_token-02.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=0388c2ea4c09bab6b29da919b8d3adfa" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/dpop_bound_access_token-02.png" />

認可サーバのトークンエンドポイントの実装は、トークンリクエストから
DPoP proof JWT を取り出します。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/dpop_bound_access_token-03.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=198e0d0633e9ec9171c212f1a636b33f" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/dpop_bound_access_token-03.png" />

その DPoP proof JWT のヘッダから公開鍵を取り出し、その公開鍵で
DPoP proof JWT の署名を検証します。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/dpop_bound_access_token-04.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=ce1f6ac42656fe25ffdef97ce8c83f50" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/dpop_bound_access_token-04.png" />

その後、トークンリクエストが有効であればアクセスストークンを生成し、それをデータベースに保存します。
ただし、アクセストークンが情報を自分自身に埋め込む形式の場合
(JWT アクセストークンなど)、データベースにデータを書き込まない実装もありえます。

このとき、ハッシュ関数として SHA-256 ([NIST FIPS 180-4][NIST_FIPS_180_4])
を用いて公開鍵の JWK Thumbprint ([RFC 7638][RFC_7638])
を計算し、アクセストークンと紐付けて覚えておきます。

このように、DPoP proof JWT に埋め込まれた公開鍵と紐付けられたアクセストークンのことを「<ruby>DPoP<rp> (</rp><rt>ディーポップ</rt><rp>)
</rp></ruby>-<ruby>bound<rp> (</rp><rt>バウンド</rt><rp>)
</rp></ruby>」アクセストークンと呼びます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/dpop_bound_access_token-05.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=ea1710287bab5b94221371da3925342f" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/dpop_bound_access_token-05.png" />

トークンエンドポイントは、生成したアクセストークンを含むトークンレスポンスをクライアントアプリケーションに返します。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/dpop_bound_access_token-06.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=c244ac6552f85144eb6a2299bd2d10f4" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/dpop_bound_access_token-06.png" />

リソースリクエストに先立ち、クライアントアプリケーションは DPoP proof JWT を作成します。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/dpop_bound_access_token-07.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=8ab21730a0fcecf3587ec395a30df9fe" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/dpop_bound_access_token-07.png" />

なお、DPoP proof JWT をアクセストークンと同時に使う場合、アクセストークンの
SHA-256 ハッシュを base64url ([RFC 4648][RFC_4648]) エンコードしたものを `ath`
クレームの値としてペイロードに含める必要があります。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/dpop_bound_access_token-08.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=ca4feed5da4180c8b2cfa0d2a133f781" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/dpop_bound_access_token-08.png" />

DPoP proof JWT が用意できたら、リソースリクエストをおこないます。
アクセストークンは `Authorization` ヘッダに、DPoP proof JWT は
`DPoP` ヘッダに埋め込みます。`Authorization` ヘッダ値のスキーム部を
`Bearer` ([RFC 6750][RFC_6750]) ではなく `DPoP` とする点に注意してください。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/dpop_bound_access_token-09.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=64f251315dfef5b67fdcd064e9cb9f3f" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/dpop_bound_access_token-09.png" />

リソースリクエストを受け取ったリソースサーバは、リクエストから DPoP proof JWT
を取り出し、そのヘッダに埋め込まれている公開鍵を用いて署名検証をおこないます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/dpop_bound_access_token-10.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=c0b67a95b08a10c38ade382239a657ba" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/dpop_bound_access_token-10.png" />

リソースサーバは認可サーバのイントロスペクションエンドポイントに問い合わせ、アクセストークンの情報を得ます。
イントロスペクションエンドポイントの実装は、アクセストークンが DPoP-bound であれば、紐付いている公開鍵の
JWK Thumbprint を、`cnf` ([RFC 7800][RFC_7800]) プロパティの `jkt`
サブプロパティの値としてイントロスペクションレスポンスに含めます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/dpop_bound_access_token-11.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=d45a30478c2734ffc0b82a5eab7f7641" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/dpop_bound_access_token-11.png" />

リソースサーバは、DPoP proof JWT に含まれる公開鍵の JWK Thumbprint
を計算し、その値がイントロスペクションレスポンス内の値と一致するかどうか確認します。
一致しなければ、リソースリクエストをおこなっているクライアントアプリケーションはアクセストークンの正当な保有者ではないと判断し、リソースリクエストを拒否します。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/dpop_bound_access_token-12.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=35a1934c817aa38a5cabfe5e1e02cfaf" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/dpop_bound_access_token-12.png" />

まとめ

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/dpop_bound_access_token.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=e8616a18b0c1e69f606d0450dd504cd7" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/dpop_bound_access_token.png" />

### DPoP Nonce

サーバは、サーバが提供する nonce 値を DPoP proof JWT に含めることを要求する場合があります。
nonce 値に生存期間を設けることでサーバが DPoP proof JWT の有効期間を制御したり
([RFC 9449 Section 8][RFC_9449_8])、nonce 値を予測不能とすることで DPoP proof JWT
を作り置きして他所で利用することを難しくしたり
([RFC 9449 Section 11.2][RFC_9449_11_2])、など、nonce
を要求することにはセキュリティ上の利点があります。

nonce を要求するリソースサーバに `nonce` クレームを含まない DPoP proof JWT
を送ると、`DPoP-Nonce` ヘッダを含む `use_dpop_nonce` エラーが返ってきます。

```text theme={null}

HTTP/1.1 401 Unauthorized

WWW-Authenticate: DPoP error="use_dpop_nonce"

DPoP-Nonce: ZgmFr7UWLrJX0fHB

```

このエラーを受けたクライアントアプリケーションは、`DPoP-Nonce` ヘッダの値を取り出し、その値を
`nonce` クレームの値として含む DPoP proof JWT を作成します。
そして、その新しく作成した DPoP proof JWT を添えてリソースリクエストを再送します。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/use_dpop_nonce.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=e80e1d2e88fae11b7d8e55caf1dcc185" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/use_dpop_nonce.png" />

クライアントアプリケーションは以降のリソースリクエストで同じ nonce 値を使い続けます。
しかし、いつかはその nonce の有効期間も終了します。そのため、同じ nonce
を使い続けると、最終的にはサーバから `invalid_dpop_proof` エラーが返ってきます。

```text theme={null}

HTTP/1.1 401 Unauthorized

WWW-Authenticate: DPoP error="invalid_dpop_proof"

DPoP-Nonce: HDoSFcMWFrfcWdr3

```

そのエラーレスポンスに `DPoP-Nonce` ヘッダが含まれており、その値がこれまで使っていた
nonce 値と異なるなら、nonce 値が古いことがエラーの原因の可能性があります。
この場合、DPoP proof JWT を作り直してリソースリクエストの再送を試みる価値があります。

## ターゲット URI

リソースサーバが DPoP をサポートする場合、受け取った DPoP proof JWT の `htu`
クレームの値が、リクエストのターゲット URI
(からクエリーパラメータとフラグメントパラメータを除いたもの)
と一致するか確認しなければなりません。この確認のため、リソースサーバはリクエストの絶対
URI を知る必要があります。

同様に、リソースサーバが HTTP メッセージ署名 ([RFC 9421][RFC_9421])
をサポートする場合、`@target-uri` 派生コンポーネント
([RFC 9421 Section 2.2.2][RFC_9421_2_2_2])
の値として用いるため、リソースサーバはリクエストの絶対 URI を知る必要があります。

しかしながら、リソースサーバがリバースプロキシの後ろで動いている場合、リソースサーバはリクエストの絶対
URI を直接知ることはできません。というのは、リソースサーバが認識する HTTP
リクエストは、クライアントアプリケーションがリバースプロキシに送ったものではなく、リバースプロキシがリソースサーバに送ったものであり、たいていの場合、スキーマ、ホスト名、ポート番号が、オリジナルのリクエスト
(クライアントアプリケーションがリバースプロキシに送ったリクエスト) とは異なるからです。

クライアントアプリケーションが送ったリクエストとリソースサーバが受け取ったリクエストは異なるため、リソースサーバが受け取ったリクエストのターゲット
URI を DPoP proof JWT の `htu` クレームの値と比較しても一致せず、DPoP
proof JWT を含むリソースリクエストは全て拒否されてしまいます。
同様に、クライアントアプリケーションから見た `@target-uri`
派生コンポーネントの値とリソースサーバが認識する `@target-uri` は一致しないため、HTTP
メッセージ署名の検証はパスせず、リソースリクエストは全て拒否されてしまいます。

リソースサーバにおける DPop proof JWT や HTTP メッセージ署名の検証は、自身が受け取ったリクエストのターゲット
URI ではなく、リバースプロキシが受け取ったリクエストのターゲット URI
に基づいておこなわなければなりません。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/target_uri.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=05ffd57a999cae8caf7ade091ba8f38f" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/target_uri.png" />

### ターゲット URI 解決

リバースプロキシが HTTP リクエストを転送する際、オリジナルの HTTP
リクエストに関する情報を含むカスタム HTTP ヘッダ群を追加するのは一般的に行われています。

この用途のため、[RFC 7239 Forwarded HTTP Extension][RFC_7239] により
`Forwarded` HTTP ヘッダが定義されました。例えば、オリジナルリクエストのスキームが `https`、
ホスト名が `rs.example.com` であることを伝える場合、次のような
`Forwarded` ヘッダが追加されるでしょう。

```text theme={null}

Forwarded: proto=https;host=rs.example.com

```

`Forwarded` HTTP ヘッダは、それまで `X-Forwarded-For`、`X-Forwarded-By`、`X-Forwarded-Proto`
などの非標準 HTTP フィールドを使って実現していたことを標準化したものです。
ですので、`Forwarded` HTTP ヘッダの利用が推奨されます。

しかしながら、`Forwarded` HTTP ヘッダの値の構文は意外と複雑で、そのパース処理を
(不可能ではないにしても) 正規表現でおこなうのは難しく、また、[RFC 8941][RFC_8941]
で定義される汎用構文とも異なるので汎用ライブラリを用いることもできません。
結局は、`Forwarded` HTTP ヘッダ専用のパース処理を書かなければなりません
(例：[http-field-parser][HTTP_FIELD_PARSER])。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/forwarded_http_field_syntax.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=8efe11c7402fb393e394e21bddf0d2a4" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/forwarded_http_field_syntax.png" />

そのため、処理しやすいカスタム HTTP ヘッダ群は依然として広く使われています。
以下は、そのようなカスタム HTTP ヘッダ群の例です。

| ヘッダ名                   | 参照                                                                                          |
| :--------------------- | :------------------------------------------------------------------------------------------ |
| `Front-End-Https`      | \[Microsoft] [Helping to Secure Communication: Client to Front-End Server][FRONT_END_HTTPS] |
| `X-Forwarded-For`      | MDN Web Docs / [X-Forwarded-For][X_FORWARDED_FOR]                                           |
| `X-Forwarded-Host`     | MDN Web Docs / [X-Forwarded-Host][X_FORWARDED_HOST]                                         |
| `X-Forwarded-Port`     | \[AWS] HTTP headers and Classic Load Balancers # [X-Forwarded-Port][X_FORWARDED_PORT]       |
| `X-Forwarded-Proto`    | MDN Web Docs / [X-Forwarded-Proto][X_FORWARDED_PROTO]                                       |
| `X-Forwarded-Protocol` | MDN Web Docs / [X-Forwarded-Proto][X_FORWARDED_PROTO]                                       |
| `X-Forwarded-Ssl`      | MDN Web Docs / [X-Forwarded-Proto][X_FORWARDED_PROTO]                                       |
| `X-Url-Scheme`         | MDN Web Docs / [X-Forwarded-Proto][X_FORWARDED_PROTO]                                       |

しかし、標準化された `Forwarded` HTTP ヘッダや広く使われているカスタム
HTTP ヘッダ群は、ターゲット URI を求めるための手段としては不完全です。
というのは、パス部やクエリー部の情報を含まないからです。

HTTP メッセージ署名の `@target-uri`
派生コンポーネントを利用するためにはクエリー部の情報も必要なのですが、残念ながら現時点では、パス部やクエリー部も含む絶対
URL の情報を伝えるための標準化されたヘッダや広く使われているカスタムヘッダはありません。
絶対 URL を表す何らかのカスタム HTTP ヘッダ (例：`X-Forwarded-URL`) の普及や、標準
HTTP ヘッダ (例：`Target-URI`) の定義、新しい
[HTTP Forwarded パラメータ][IANA_HTTP_FORWARDED_PARAMETERS]の追加などが望まれます。

## HTTP メッセージ署名

HTTP メッセージは、幾つもの中継サーバを経由して届けられるのが一般的です。
その中継処理の間に、TLS 終端や HTTP ヘッダ群の追加・統合・変更などもよく行われます。
このような条件下において、送信者と受信者の間で (End-to-End で)、HTTP メッセージの完全性
(integrity) と真正性 (authenticity) を保証する方法が長年検討されてきました。

一般的に、完全性と真正性の実現方法はデジタル署名です。しかし、HTTP
メッセージの場合、中継処理の途中で部分的に変更されうるので、単純に HTTP
メッセージ全体を対象としてデジタル署名をおこなう方法は機能しません。
そのため、送信者と受信者の双方にとって意味のある部分のみを選択し、中継サーバによる変更の影響を受けないように正規化をおこない、その部分集合に対して署名をおこなう方法が必要になります。

この目的のため、かなりの数の競合する仕様案が提案されました。
実際に実装・運用される仕様案もありました。そして、最終的に IETF が採択したものが
[RFC 9421 HTTP Message Signatures][RFC_9421] となりました。

### HTTP メッセージコンポーネント

RFC 9421 では、署名対象となりうる HTTP メッセージの部分を HTTP メッセージコンポーネントと呼んでいます。

最も分かりやすい HTTP メッセージコンポーネントは、HTTP フィールドです。
例えば、`Content-Type` HTTP フィールドや `Date` HTTP フィールドは、それぞれ
HTTP メッセージコンポーネントの一種です。

一方、HTTP フィールド以外の属性から派生する HTTP メッセージコンポーネントもあり、派生コンポーネント
(derived component) と呼ばれます。HTTP メソッドやターゲット URI、HTTP
ステータスコードは派生コンポーネントの一種です。

どのコンポーネントを署名対象とするかは、アプリケーションがそれぞれの目的に応じて自由に選択します。
例えば、[FAPI 2.0 Http Signatures][FAPI2_HTTP_SIGNATURES] という仕様では、HTTP
リクエストに HTTP メッセージ署名をおこなう際、下記のコンポーネント群を署名対象とすると定めています。

1. HTTP メソッド
2. ターゲット URI
3. `Authorization` HTTP フィールド
4. `DPoP` HTTP フィールド (存在する場合)
5. `Content-Digest` HTTP フィールド (メッセージボディを持つ場合)

### シグネチャベース

HTTP メッセージ署名では、デジタル署名の入力となる文字列をシグネチャベース (Signature Base) と呼びます。
シグネチャベースは、署名対象の HTTP メッセージコンポーネントの識別子と値の組を列挙し、末尾に署名に関するメタデータを付加したものです。

HTTP メッセージ署名の生成や検証に先立って、このシグネチャベースを組み立てる必要があります。
シグネチャベースの正確な構文については [RFC 9421][RFC_9421] の
[Section 2.5. Creating the Signature Base][RFC_9421_2_5] を参照してください。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/signature_base_syntax.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=f2ff7c7a70885621fdb89e0c7c2f1802" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/signature_base_syntax.png" />

下記は [RFC 9421][RFC_9421] から抜粋したシグネチャベースの例です。

```text theme={null}

"@method": POST

"@authority": example.com

"@path": /foo

"content-digest": sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:

"content-length": 18

"content-type": application/json

"@signature-params": ("@method" "@authority" "@path" "content-digest" "content-length" "content-type");created=1618884473;keyid="test-key-rsa-pss"

```

このシグネチャベースの例では、1 行目から 3 行目が派生コンポーネントの識別子と値の組となっています。派生コンポーネントのコンポーネント名は、`@method` のように `@` で始まります。一方、4 行目から 6 行目は HTTP フィールドとその値の組です。

シグネチャベースの最後の行 (この例では 7 行目) には、シグネチャのメタデータ情報が置かれます。必ず `"@signature-params":` で始まり、その後に内部リスト ([RFC 8941 Section 3.1.1][RFC_8941_3_1_1]) が続きます。この内部リストには、署名対象の HTTP メッセージコンポーネント群のコンポーネント識別子 (component identifier) が列挙されます。この例では次の 6 つが含まれています。

* `"@method"`
* `"@authority"`
* `"@path"`
* `"content-digest"`
* `"content-length"`
* `"content-type"`

丸括弧で囲まれたコンポーネント識別子群の後ろに続く、セミコロン (`;`)
で始まる部分は、内部リストのオプショナルパラメータ群です。
この例では、`created` パラメータと `keyid` パラメータが含まれています。
これらのパラメータは [RFC 9421 Section 2.3][RFC_9421_2_3] で定義されています。

### 署名メソッド

[RFC 9421 Section 3.3][RFC_9421_3_3] で `HTTP_SIGN`
と抽象的に表現される署名メソッドは、シグネチャベース (`M`) と署名鍵 (`Ks`)
を入力として受け取り、署名 (`S`) を出力します。

```
HTTP_SIGN (M, Ks) -> S
```

[RFC 9421 Section 3.3][RFC_9421_3_3] には署名アルゴリズムが列挙されており、IANA の
[HTTP Signature Algorithms レジストリ][IANA_HTTP_SIGNATURE_ALGORITHMS]には次のアルゴリズム群が登録されています。

| アルゴリズム名             | 説明                                      | 参照                                        |
| :------------------ | :-------------------------------------- | :---------------------------------------- |
| `rsa-pss-sha512`    | RSASSA-PSS using SHA-512                | [RFC 9421, Section 3.3.1][RFC_9421_3_3_1] |
| `rsa-v1_5-sha256`   | RSASSA-PKCS1-v1\_5 using SHA-256        | [RFC 9421, Section 3.3.2][RFC_9421_3_3_2] |
| `hmac-sha256`       | HMAC using SHA-256                      | [RFC 9421, Section 3.3.3][RFC_9421_3_3_3] |
| `ecdsa-p256-sha256` | ECDSA using curve P-256 DSS and SHA-256 | [RFC 9421, Section 3.3.4][RFC_9421_3_3_4] |
| `ecdsa-p384-sha384` | ECDSA using curve P-384 DSS and SHA-384 | [RFC 9421, Section 3.3.5][RFC_9421_3_3_5] |
| `ed25519`           | EdDSA using curve edwards25519          | [RFC 9421, Section 3.3.6][RFC_9421_3_3_6] |

OAuth 2.0 や OpenID Connect の文脈で馴染みのある JWS アルゴリズム群については、RFC
9421 の [Section 3.3.7. JSON Web Signature (JWS) Algorithms][RFC_9421_3_3_7]
で明示的に言及されています。これらのアルゴリズムを用いる場合、シグネチャベースを
JWS Signing Input ([RFC 7515][RFC_7515]) として用いることと定められています。

HTTP メッセージ署名には JWS ヘッダに相当するものが存在しないので、署名アルゴリズムを別の方法で署名検証者に伝える必要があります。
この目的のため、[RFC 9421 Section 2.3][RFC_9421_2_3] で定義されている `alg`
パラメータを用いるのが適切に思えます。しかしながら、[RFC 9421 Section 3.3.7][RFC_9421_3_3_7]
の最終段落でわざわざ次のように述べているため、`alg` パラメータは用いません。

> JSON Web Algorithm (JWA) values from the "JSON Web Signature and Encryption
> Algorithms" registry are not included as signature parameters. Typically, the
> JWS algorithm can be signaled using JSON Web Keys (JWKs) or other mechanisms
> common to JOSE implementations. In fact, JWA values are not registered in the
> "HTTP Signature Algorithms" registry (Section 6.2), and so the explicit `alg`
> signature parameter is not used at all when using JOSE signing algorithms.
>
> (日本語訳) 「JSON Web Signature and Encryption Algorithms」レジストリにある JSON
> Web Algorithm (JWA) の値は、署名パラメータには含まれません。通常、JWS アルゴリズムは
> JSON Web Key (JWK) や、JOSE 実装で一般的に用いられるその他の仕組みによって通知されます。
> 実際、JWA の値は「HTTP Signature Algorithms」レジストリ (セクション 6.2)
> には登録されていないため、JOSE の署名アルゴリズムを使用する際に署名パラメータとして明示的な
> `alg` が使われることはありません。

### 検証メソッド

[RFC 9421 Section 3.3][RFC_9421_3_3] で `HTTP_VERIFY`
と抽象的に表現される検証メソッドは、シグネチャベース (`M`)、検証鍵 (`Kv`)、署名 (`S`)
を入力として受け取り、検証結果 (`V`) を出力します。

```
HTTP_VERIFY (M, Kv, S) -> V
```

検証処理の入力のうち、検証鍵は HTTP メッセージに含まれていないため、何らかの方法で入手する必要がありますが、
[RFC 9421][RFC_9421] はその入手方法を定めていません。

HTTP リクエストの HTTP メッセージ署名をリソースサーバが検証しようとする場合、一見、次の手順で検証鍵を入手できそうに思えます。

1. HTTP リクエストに含まれるアクセストークンを取り出す
2. アクセストークンに紐付くクライアントアプリケーションを特定する
3. クライアントアプリケーションの `jwks_uri` メタデータの情報を取得する
4. `jwks_uri` が指す場所から JWK Set を取得する
5. シグネチャパラメータ `keyid` の値に基づき、JWK Set 内にある検証鍵を特定する

しかし、よくよく検討してみると、リソースサーバの文脈では、標準仕様だけではクライアントアプリケーションの
`jwks_uri` メタデータの値を取得できないことが分かります。
個人による仕様案を除くと、唯一利用可能な標準仕様は [OpenID Federation][OIDFED]
([解説記事][OIDFED_ARTICLE]) のみですが、この仕様の実装と運用は重い作業であり、
当仕様が広く普及しているとも言い難いので、検証鍵の入手方法としては汎用解とはなりません。

そのため、現状では、リソースサーバが HTTP リクエストの HTTP メッセージ署名を検証するためには、標準化されていない実装固有の方法で検証鍵を入手しなければなりません。

<Note>
  なお、FAPI 2.0 の文脈に限定すれば、標準仕様のみを用いて検証鍵を決定する方法があります。
  詳細は後述します。
</Note>

### 署名付加

HTTP メッセージ署名は、`Signature` HTTP フィールドと `Signature-Input` HTTP
フィールドを用いて HTTP メッセージに付加します。

`Signature` HTTP フィールドの値のフォーマットはディクショナリ
([RFC 8941 Section 3.2][RFC_8941_3_2]) です。
個々のキー・バリューの組は、任意のラベルと、バイトシーケンス
([RFC 8491 Section 3.3.5][RFC_8941_3_3_5]) で表現された署名です。

```text theme={null}

Signature: ラベル=:Base64エンコードされた署名:, ラベル=:Base64エンコードされた署名:, ...

```

`Signature-Input` HTTP フィールドの値のフォーマットもディクショナリ
([RFC 8941 Section 3.2][RFC_8941_3_2]) です。
個々のキー・バリューの組は、任意のラベルと、内部リスト
([RFC 8941 Section 3.1.1][RFC_8941_3_1_1]) で表現された署名メタデータです。

```text theme={null}

Signature-Input: ラベル=(コンポーネント識別子群)任意パラメータ群, ラベル=(コンポーネント識別子群)任意パラメータ群, ...

```

ラベルは任意に付けられますが、`Signature` HTTP フィールド内の使われたラベルと同じラベルが
`Signature-Input` HTTP フィールド内にも存在しなければなりません。

下記は [RFC 9421 Section 4.3][RFC_9421_4_3] から抜粋した `Signature` HTTP
フィールドと `Signature-Input` HTTP フィールドを含む HTTP リクエストの例です。

```text theme={null}

POST /foo?param=Value&Pet=dog HTTP/1.1

Host: example.com

Date: Tue, 20 Apr 2021 02:07:55 GMT

Content-Type: application/json

Content-Length: 18

Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:

Signature-Input: sig1=("@method" "@authority" "@path" "content-digest" "content-type" "content-length");created=1618884475;keyid="test-key-ecc-p256"

Signature: sig1=:X5spyd6CFnAG5QnDyHfqoSNICd+BUP4LYMz2Q0JXlb//4Ijpzp+kve2w4NIyqeAuM7jTDX+sNalzA8ESSaHD3A==:


{"hello": "world"}

```

## FAPI 2.0 Http Signatures

[RFC 9421][RFC_9421] 自身が述べているように、当仕様はツールとして設計されているため、実際に利用する際はアプリケーションやプロファイルの目的に合わせて要件を追加する必要があります。
[RFC 9421][RFC_9421] の [Section 1.4][RFC_9421_1_4] にはそのような要件が例示されています。
例えば、次のような項目が挙げられています。

* 署名対象とするコンポーネント群
* 署名メタデータのパラメータ群
* 署名鍵、検証鍵の取得方法
* 署名アルゴリズム

FAPI 2.0 仕様ファミリーの一つである [FAPI 2.0 Http Signatures][FAPI2_HTTP_SIGNATURES]
は、リソースサーバへのリソースリクエストとリソースサーバからリソースレスポンスに HTTP
メッセージ署名を適用するために [RFC 9421][RFC_9421] をプロファイリングした仕様で、具体的な要件を定めています。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/fapi2_specification_family.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=8662de593ef842132b2b4be9115298ac" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/fapi2_specification_family.png" />

仕様の要約は次の通りです。なお、仕様策定作業中のため、今後仕様が変更される可能性があります。

\| リクエスト署名 |
\| コンポーネント | "@method" |  |
\| "@target-uri" |  |
\| "authorization" |  |
\| "dpop" | DPoP が使われている場合 |
\| "content-digest" | メッセージボディがある場合 |
\| メタデータパラメータ | created |  |
\| tag | 値は "fapi-2-request" で固定 |

\| レスポンス署名 |
\| コンポーネント | "@method";req |  |
\| "@target-uri";req |  |
\| "authorization";req |  |
\| "dpop";req | DPoP が使われている場合 |
\| "content-digest";req | リクエストにメッセージボディがある場合 |
\| "@status" |  |
\| "content-digest" | レスポンスにメッセージボディがある場合 |
\| メタデータパラメータ | created |  |
\| tag | 値は "fapi-2-response" で固定 |

<Note>
  `Content-Digest` HTTP フィールドは
  [RFC 9530 Digest Fields](https://www.rfc-editor.org/rfc/rfc9530.html) の
  [Section 2.
  The `Content-Digest` Field](https://www.rfc-editor.org/rfc/rfc9530.html#section-2) で定義されています。
</Note>

### リクエスト署名の検証鍵

[FAPI 2.0 Http Signatures][FAPI2_HTTP_SIGNATURES] 仕様を用いる場合、
[FAPI 2.0 Security Profile][FAPI2_SECURITY]
仕様も適用されるため、リソースリクエストの際は MTLS または DPoP
を用いた送信者限定アクセストークンが用いられます。

MTLS と DPoP のどちらの場合でも、リソースサーバにはクライアントアプリケーションの公開鍵が渡ってきます。
この技術的特性を念頭に置き、ここで「送信者限定アクセストークンに紐付いた公開鍵とペアとなっている秘密鍵で
HTTP リクエストに署名する」という約束事を導入すると「[検証メソッド](#http-message-signature-verification-method)」で言及した検証鍵取得問題を解決することができます。

この解決方法の良い点は、承認済みの標準仕様 ([RFC 8705][RFC_8705] と [RFC 9449][RFC_9449])
のみで検証鍵を取得できることです。標準化されていない実装固有の方法を避けることができるため、相互運用性が高まります。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/request_verification_key_retrieval_options.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=eb65c448b22f5cae61f5a223810685b4" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/request_verification_key_retrieval_options.png" />

<Note>
  「送信者限定アクセストークンに紐付いた公開鍵とペアとなっている秘密鍵で
  HTTP リクエストに署名する」というのは
  [FAPI
  2.0 Http Signatures](https://openid.bitbucket.io/fapi/fapi-2_0-http-signatures.html) 仕様の一部ではなく、一つの案に過ぎないので注意してください。
  仕様には *"This specification doesn't specify the exact means by which
  a resource server can retrieve the key for the client."*
  *(この仕様はリソースサーバがクライアントの鍵を取得するための正確な方法を規定していません。)*
  と明記されています。
</Note>

### レスポンス署名の検証鍵

クライアントアプリケーションがリソースレスポンスの HTTP
メッセージ署名を検証するためには、リソースサーバが提供する検証鍵を入手する必要があります。

クライアントアプリケーションによる検証鍵提供に比べ、リソースサーバによる検証鍵提供は分かりやすい方法で実現できます。
認可サーバや OpenID プロバイダがそうしているように、リソースサーバも JWK Set ドキュメント
([RFC 7517][RFC_7517]) を公開し、その文書内に HTTP メッセージ署名を検証するための検証鍵を入れておけばよいのです。

JWK Set ドキュメントの公開場所については、
`/.well-known/oauth-protected-resource`
で公開するリソースサーバメタデータ ([RFC 9728][RFC_9728]) 内の `jwks_uri`
プロパティにより示すことができます。

JWK Set ドキュメントにより検証鍵を提供する場合、複数存在する鍵の中から検証鍵を特定することを可能とするため、鍵
(JWK) および HTTP メッセージ署名の双方に同じ鍵識別子を付けておくのがよいでしょう。
鍵には `kid` プロパティ ([RFC 7517 Section 4.5][RFC_7517_4_5]) で、HTTP
メッセージ署名には `keyid` パラメータ ([RFC 9421 Section 2.3][RFC_9421_2_3])
で鍵識別子を関連付けることができます。

<img src="https://mintcdn.com/authlete/KNbgpS77nJg5j2Vw/img/kb/ja/api_protection/response_verification_key_retrieval.png?fit=max&auto=format&n=KNbgpS77nJg5j2Vw&q=85&s=0769fbc6f11d9d5e15113f9e090b4a19" alt="" width="1920" height="1080" data-path="img/kb/ja/api_protection/response_verification_key_retrieval.png" />

## RAR

アクセストークンのスコープ ([RFC 6749 Section 3.3][RFC_6749_3_3])
は、そのアクセストークンが持つ粗粒度の権限を表しています。

より細かく権限を表現するため、認可サーバの実装によっては、スコープ名に構造を持たせて一部を変数として扱う仕組みを導入しました。
そのような仕組みはパラメータ化スコープ (parameterized scope) や動的スコープ (dynamic scope)
などと呼ばれます。もちろん実装間に互換性はありません。

実装が幾つもあるとはいえ、スコープ名に情報を詰め込むのは、率直に言うとバッドプラクティスです。
この状況を改善するために [RFC 9396 OAuth 2.0 Rich Authorization Requests][RFC_9396]
(通称 <ruby>RAR<rp> (</rp><rt>ラー</rt><rp>) </rp></ruby>) が策定されました。
RAR 仕様は、権限を構造的に表現するための汎用的な仕組みとして、`authorization_details`
JSON 配列を定義しました。

`authorization_details` JSON 配列の各要素は JSON オブジェクト
(以降 RAR オブジェクト) です。RAR オブジェクトが必ず持たなければならない唯一のプロパティは
`type` です。この `type` プロパティは、その RAR オブジェクトがどのような構造を持っているかを示します。

[RFC 9396][RFC_9396] 自体は `type` プロパティの値を定めていません。
どのような値を用いるかは、アプリケーションやプロファイルが独自に決めます。
例えば、[OpenID for Verifiable Credential Issuance][OID4VCI]
(OID4VCI) 仕様では `openid_credential` という値を定義しました。
以下は同仕様から抜粋した `authorization_details` 配列の例です。

```json theme={null}
[
  {
    "type": "openid_credential",
    "credential_configuration_id": "UniversityDegreeCredential"
  }
]
```

RAR 仕様は、`type` プロパティの他に次のようなプロパティ群を定義しています。
これらを利用するか否かは自由です。

* `locations`
* `actions`
* `datatypes`
* `identifier`
* `privileges`

先ほど挙げた OID4VCI 仕様での利用例も示す通り、アプリケーションやプロファイルが
RAR オブジェクトに独自のプロパティを追加してもかまいません。下記は [RFC 9396][RFC_9396]
から抜粋した `authorization_details` 配列の例です。
`geolocation` や `currency` といった、[RFC 9396][RFC_9396]
では定義していないトップレベルプロパティが使われています。

```json theme={null}
[
  {
    "type": "photo-api",
    "actions": [
      "read",
      "write"
    ],
    "locations": [
      "https://server.example.net/",
      "https://resource.local/other"
    ],
    "datatypes": [
      "metadata",
      "images"
    ],
    "geolocation": [
      {
        "lat": -32.364,
        "lng": 153.207
      },
      {
        "lat": -35.364,
        "lng": 158.207
      }
    ]
  },
  {
    "type": "financial-transaction",
    "actions": [
      "withdraw"
    ],
    "identifier": "account-14-32-32-3",
    "currency": "USD"
  }
]
```

RAR 仕様によれば、`scope` パラメータが利用可能な全ての箇所で (実装がサポートしていれば)
`authorization_details` パラメータを使えるとされています。
リソースサーバにとっては、イントロスペクションレスポンスや JWT
アクセストークンのペイロード部の `authorization_details`
パラメータが重要になります。

下記は [RFC 9396 Section 9.2. Token Introspection][RFC_9396_9_2]
から抜粋したイントロスペクションレスポンスの例です。

```json theme={null}
{
  "active": true,
  "sub": "24400320",
  "aud": "s6BhdRkqt3",
  "exp": 1311281970,
  "acr": "psd2_sca",
  "txn": "8b4729cc-32e4-4370-8cf0-5796154d1296",
  "authorization_details": [
    {
      "type": "https://scheme.example.com/payment_initiation",
      "actions": [
        "initiate",
        "status",
        "cancel"
      ],
      "locations": [
        "https://example.com/payments"
      ],
      "instructedAmount": {
        "currency": "EUR",
        "amount": "123.50"
      },
      "creditorName": "Merchant123",
      "creditorAccount": {
        "iban": "DE02100100109307118603"
      },
      "remittanceInformationUnstructured": "Ref Number Merchant"
    }
  ],
  "debtorAccount": {
    "iban": "DE40100100103307118608",
    "user_role": "owner"
  }
}
```

## Cedar

[Cedar][CEDAR] は認可ポリシーを記述するための言語です
([Cedar Policy Language Reference Guide][CEDAR_REFERENCE])。Cedar で記述された認可ポリシー
(以降 Cedar ポリシー) を、リソースアクセスを許可するかどうかの判定に利用することができます。

Cedar の大きな利点の一つ目は、認可ロジックをビジネスロジックから切り離すことができることです。Cedar
ポリシーは外部化が可能で、ビジネスロジックとは別の場所で管理することができます。実際に Cedar
ポリシーを管理する商用サービスも存在します ([Integrations][CEDAR_INTEGRATIONS] 参照)。

Cedar の大きな利点の二つ目は、細かい粒度で認可ロジックを記述できることです。
例えば、「Jane の友達であれば、Jane の旅行アルバム内の写真を見てコメントすることを許可する」という認可ロジックを記述できます。
下記は [Cedar Policy Language Reference Guide][CEDAR_REFERENCE] の
[Example scenario][CEDAR_EXAMPLE_SCENARIO] から抜粋した Cedar
ポリシーの例で、この認可ロジックを表現しています。

```text theme={null}
permit (
principal in Group::"janeFriends",
    action in [Action::"view", Action::"comment"],
    resource in Album::"janeTrips"
);
```

Cedar ポリシーには、`when` 節や `unless` 節を足してポリシーを適用する条件を指定することができます。
それらの条件の中で、比較演算子、論理演算子、算術演算子、などを使うこともでき、高い表現力があります。

```text theme={null}
permit (
principal,
    action == Action::"read",
    resource
)
when {
resource.owner == principal ||
    resource.tag == "public"
};
```

### Cedar in RAR オブジェクト

アクセストークンにスコープ ([RFC 6749 Section 3.3][RFC_6749_3_3])
を紐付けることで、そのアクセストークンの粗粒度の権限を表現することができます。
同様にして、もしもアクセストークンに Cedar ポリシーを紐付けることができれば、
そのアクセストークンの細粒度の権限を表現することができるのではないでしょうか？

このアイディアを実現する手段として有力視されているのが、Cedar ポリシーを RAR
オブジェクトとして表現する方法です。

表現方法の詳細を決めるにあたり、考慮すべき点として次のものがあります。

* Cedar ポリシーのオリジナルの記法は独特。その独特の記法のまま RAR
  オブジェクトに埋め込むべきか、それとも JSON 形式
  ([JSON Policy Format][CEDAR_JSON_FORMAT]) に変換して埋め込むべきか？
  JSON 形式は RAR との親和性は高いが、冗長であるため (特に `when`/`unless`
  条件部の表現)、Cedar コミュニティの大半の人々はオリジナル記法の方を好むと思われる。

* Cedar ポリシーのオリジナル記法と JSON 形式、両方サポートすべきか、それともどちらかに限定するべきか？

* 両方の書式をサポートする場合、どちらの書式が使われているかをどのように示すべきか？
  `type` プロパティの値に書式の情報を埋め込むか、それとも書式を示すプロパティ
  (`format` 等) を別途設けるべきか？

* ポリシーセットを一つの RAR オブジェクトとして表現すべきか、それとも一つの RAR
  オブジェクトで一つのポリシーのみを表現するべきか？
  「[Representing a policy set with JSON][CEDAR_POLICY_SET_FORMAT]」で示されているポリシーセットの
  JSON 表現では、一つの JSON オブジェクト内に `staticPolicies`、`templates`、`templateLinks`
  が含まれているが、この表現方法に倣うべきか？ そもそも RAR の文脈で `templates` のサポートは必要なのか？

* Cedar ポリシー以外のもの (例えば Cedar スキーマ) を表現する必要に迫られる可能性はあるか？
  その可能性を見越したとき、`type` の値はどうすべきか？

下記は "Cedar in RAR Object" 仕様案の一例です。

\| Cedar in RAR Object 仕様案 |
\| プロパティ | type | 必須 | 値は "cedar-policy" で固定。 |
\| format | 任意 | "cedar" (デフォルト) または "json"。 |
\| policy\_id | 任意 | ポリシー ID。JSON 文字列。 |
\| policy | 必須 | Cedar ポリシー。format プロパティの値が "cedar"
であれば、Cedar オリジナル記法で書かれた Cedar ポリシーを含む単一の JSON 文字列。
format プロパティの値が "json"
であれば、[JSON
Policy Format](https://docs.cedarpolicy.com/policies/json-format.html) で記述された単一の Cedar ポリシーを表す JSON オブジェクト。 |
\| エンティティ | principal | アクセストークンにサブジェクトが紐付いていれば (イントロスペクションレスポンスや JWT
アクセストークンのペイロードに sub クレームがあれば)、principal
のエンティティは User。そうでなければ Client。

User エンティティの場合はアクセストークンの sub の値を一意識別子として扱う。
Client エンティティの場合はアクセストークンの client\_id の値を一意識別子として扱う。

アクセストークンの groups、roles、entitlements
([RFC 9068
Section 2.2.3](https://www.rfc-editor.org/rfc/rfc9068.html#section-2.2.3)) の各要素はそれぞれ Group、Role、Entitlement
エンティティにマッピングされ、principal の親エンティティとして設定される。
[RFC 7643
Section 2.4](https://www.rfc-editor.org/rfc/rfc7643.html#section-2.4) に従っていれば、各要素の値は JSON オブジェクトであり、value
プロパティを持っているはずである
(例: "groups":\[{'{"value":"group1"}'}])。
その value プロパティの値をエンティティの一意識別子として扱う。
[RFC 7643
Section 2.4](https://www.rfc-editor.org/rfc/rfc7643.html#section-2.4) に従っていない場合、JSON 文字列と仮定し
(例: "groups":\["group1"])、その文字列の値をエンティティの一意識別子として扱う。 |
\| action | action のエンティティはリソースサーバが定義する。 |
\| resource | resource のエンティティはリソースサーバが定義する。 |
\| コンテキスト | time | Unix epoch からの経過ミリ秒数で表現された現在時刻。 |
\| access\_token | アクセストークンの情報。サブプロパティとして
iss、sub、client\_id を持つ。 |
\| ip\_address | クライアントの IP アドレス。 |

下記は、この仕様案に基づいて記述された `authorization_details`
配列の例です。一つ目の RAR オブジェクトは Cedar オリジナル記法で Cedar ポリシーを記述し、二つ目の
RAR オブジェクトは [JSON Policy Format][CEDAR_JSON_FORMAT] で Cedar ポリシーを記述しています。

```json theme={null}
[
  {
    "type":      "cedar-policy",
    "format":    "cedar",
    "policy_id": "policy-0",
    "policy":    "permit (principal == User::\"12UA45\", action == Action::\"view\", resource);" 
  },
  {
    "type":      "cedar-policy",
    "format":    "json",
    "policy_id": "policy-1",
    "policy": {
      "effect":  "permit",
      "principal": { "op": "==", "entity": { "type": "User",   "id": "12UA45" } },
      "action":    { "op": "==", "entity": { "type": "Action", "id": "view"   } },
      "resource":  { "op": "All" }
    }
  }
]
```

<Note>
  この仕様案について業界内で合意がとれているわけではないので注意してください。
  ただし、Authlete 社ではこの仕様が実装可能で、実際に想定通りに動作することを確認済みです。

  今後リリース予定の
  [Shared Signals Framework](https://openid.net/specs/openid-sharedsignals-framework-1_0.html) 用の新製品 (Authlete Arena) では、MTLS や DPoP、FAPI
  2.0 Http Signatures といった高度なセキュリティ仕様に加え、Cedar in RAR Object
  もサポートされます。
</Note>

## ステップアップ認証

リソースサーバが提供する API の中には、他の API よりも高いセキュリティ要件を設けているものがあるかもしれません。
例えば、残高照会 API よりも送金 API の方が高いセキュリティを要求するのは一般的です。

そのようなセキュリティ要件の中で、「アクセストークン取得時に通常よりも高いユーザ認証要件を満たさなければならない」というものがありえます。

提示されたアクセストークンが取得された際に実行されたユーザ認証が、求める基準に満たない場合、API
はその旨をクライアントアプリケーションに通知し、アクセストークンの再取得を促したいと思うでしょう。
このようなケースで利用できるのが、
[RFC 9470 OAuth 2.0 Step Up Authentication Challenge Protocol][RFC_9470] です。

API は、エラーコードとして `insufficient_user_authentication`
を用いることで、アクセストークン取得時のユーザ認証が要件を満たしていないことを通知できます。
エラーレスポンスには、`acr_values` パラメータや
`max_age` パラメータを用いて要件に関する情報を追加することができます。

下記は [RFC 9470][RFC_9470] から抜粋したエラーレスポンスの例です。

```text theme={null}
HTTP/1.1 401 Unauthorized

WWW-Authenticate: Bearer error="insufficient_user_authentication",
  error_description="A different authentication level is required.",
  acr_values="myACR"
```

[RFC 9470][RFC_9470] については『[RFC 9470 OAuth 2.0 ステップアップ認証チャレンジプロトコル][STEPUP_AUTHN_ARTICLE]』で詳しく解説しているので、そちらを参照してください。

## Authorization API

[OIDF][OIDF] の [AuthZEN ワーキンググループ][OIDF_AUTHZ_WG] では
[Authorization API 1.0][AUTHZEN] 仕様の開発が進められています。

この仕様の中心となるのは Access Evaluation API です。
アクセスを許可するかどうかを判定するのに必要な情報をこの API に渡すと、判定結果を真偽値で返してくれます。
下記は、この API に対するリクエストとレスポンスの例です。

```json theme={null}
{
  "subject": {
    "type": "user",
    "id": "alice@acmecorp.com"
  },
  "resource": {
    "type": "account",
    "id": "123"
  },
  "action": {
    "name": "can_read",
    "properties": {
      "method": "GET"
    }
  },
  "context": {
    "time": "1985-10-26T01:22-07:00"
  }
}
```

```json theme={null}
{
  "decision": true
}
```

入力するパラメータはサブジェクト、リソース、アクション、コンテキストで、出力されるパラメータは判定結果です。
入出力の構成要素は正しいとは思うものの、直感としては、入力要素を全て一極集中で管理しているサーバ
(ユーザの管理もリソースの管理も一手に担っているサーバ) でなければ Access Evaluation API
を実装できなさそうに思えます。今後どのような実装が出てくるのかに注目です。

## おわりに

2012 年に公開された OAuth 2.0 の中心仕様である [RFC 6749][RFC_6749] の時点では、API
アクセス制御に利用できるのはスコープ程度しかありませんでしたが、その後、様々な機能が追加されていきました。

中でも、MTLS や DPoP による送信者限定は重要であり、FAPI 2.0 ではどちらかの実装が必須とされています。
2022 年 4 月、Heroku / Travis-CI から盗まれたアクセストークンが他所 (GitHub)
で悪用される事件が発生しましたが、この事件は送信者限定機能の重要性を知らしめました。

Authlete 社は、[RFC 9449][RFC_9449] (DPoP) や
[FAPI 2.0 Security Profile][FAPI2_SECURITY]、[FAPI 2.0 Http Signatures][FAPI2_HTTP_SIGNATURES]
などの高度なセキュリティ関連仕様の策定作業に貢献しています
(著者として仕様書に名前が記載されています)。また、それらの商用実装を提供しています。

API 保護の専門知識や商用実装をお求めの方は、是非[お問い合せ][CONTACT]ください。

{/* IETF: Internet Engineering Task Force */}

[IETF]: https://www.ietf.org/

[IETF_HTTPBIS_WG]: https://datatracker.ietf.org/wg/httpbis/about/

[IETF_OAUTH_WG]: https://datatracker.ietf.org/wg/oauth/about/

[IETF_SECEVENT_WG]: https://datatracker.ietf.org/wg/secevent/about/

[RFC_4648]: https://www.rfc-editor.org/rfc/rfc4648.html

[RFC_5280]: https://www.rfc-editor.org/rfc/rfc5280.html

[RFC_6749]: https://www.rfc-editor.org/rfc/rfc6749.html

[RFC_6749_3_3]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.3

[RFC_6749_4_1]: https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1

[RFC_6749_4_1_1]: https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1.1

[RFC_6750]: https://www.rfc-editor.org/rfc/rfc6750.html

[RFC_6750_2_1]: https://www.rfc-editor.org/rfc/rfc6750.html#section-2.1

[RFC_6750_2_2]: https://www.rfc-editor.org/rfc/rfc6750.html#section-2.2

[RFC_6750_2_3]: https://www.rfc-editor.org/rfc/rfc6750.html#section-2.3

[RFC_6755]: https://www.rfc-editor.org/rfc/rfc6755.html

[RFC_6819]: https://www.rfc-editor.org/rfc/rfc6819.html

[RFC_7009]: https://www.rfc-editor.org/rfc/rfc7009.html

[RFC_7239]: https://www.rfc-editor.org/rfc/rfc7239.html

[RFC_7468]: https://www.rfc-editor.org/rfc/rfc7468.html

[RFC_7515]: https://www.rfc-editor.org/rfc/rfc7515.html

[RFC_7515_4_1_1]: https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.1

[RFC_7515_4_1_4]: https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.4

[RFC_7517]: https://www.rfc-editor.org/rfc/rfc7517.html

[RFC_7517_4_2]: https://www.rfc-editor.org/rfc/rfc7517.html#section-4.2

[RFC_7517_4_4]: https://www.rfc-editor.org/rfc/rfc7517.html#section-4.4

[RFC_7517_4_5]: https://www.rfc-editor.org/rfc/rfc7517.html#section-4.5

[RFC_7517_5]: https://www.rfc-editor.org/rfc/rfc7517.html#section-5

[RFC_7519]: https://www.rfc-editor.org/rfc/rfc7519.html

[RFC_7519_4_1_1]: https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.1

[RFC_7519_4_1_3]: https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.3

[RFC_7519_4_1_4]: https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4

[RFC_7519_4_1_5]: https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.5

[RFC_7519_4_1_6]: https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.6

[RFC_7521]: https://www.rfc-editor.org/rfc/rfc7521.html

[RFC_7522]: https://www.rfc-editor.org/rfc/rfc7522.html

[RFC_7523]: https://www.rfc-editor.org/rfc/rfc7523.html

[RFC_7591]: https://www.rfc-editor.org/rfc/rfc7591.html

[RFC_7592]: https://www.rfc-editor.org/rfc/rfc7592.html

[RFC_7636]: https://www.rfc-editor.org/rfc/rfc7636.html

[RFC_7638]: https://www.rfc-editor.org/rfc/rfc7638.html

[RFC_7662]: https://www.rfc-editor.org/rfc/rfc7662.html

[RFC_7662_2_2]: https://www.rfc-editor.org/rfc/rfc7662.html#section-2.2

[RFC_7800]: https://www.rfc-editor.org/rfc/rfc7800.html

[RFC_8176]: https://www.rfc-editor.org/rfc/rfc8176.html

[RFC_8252]: https://www.rfc-editor.org/rfc/rfc8252.html

[RFC_8414]: https://www.rfc-editor.org/rfc/rfc8414.html

[RFC_8417]: https://www.rfc-editor.org/rfc/rfc8417.html

[RFC_8628]: https://www.rfc-editor.org/rfc/rfc8628.html

[RFC_8693]: https://www.rfc-editor.org/rfc/rfc8693.html

[RFC_8705]: https://www.rfc-editor.org/rfc/rfc8705.html

[RFC_8705_3]: https://www.rfc-editor.org/rfc/rfc8705.html#section-3

[RFC_8707]: https://www.rfc-editor.org/rfc/rfc8707.html

[RFC_8707_2]: https://www.rfc-editor.org/rfc/rfc8707.html#section-2

[RFC_8707_2_1]: https://www.rfc-editor.org/rfc/rfc8707.html#section-2.1

[RFC_8725]: https://www.rfc-editor.org/rfc/rfc8725.html

[RFC_8935]: https://www.rfc-editor.org/rfc/rfc8935.html

[RFC_8936]: https://www.rfc-editor.org/rfc/rfc8936.html

[RFC_8941]: https://www.rfc-editor.org/rfc/rfc8941.html

[RFC_8941_3_2]: https://www.rfc-editor.org/rfc/rfc8941.html#section-3.2

[RFC_8941_3_1_1]: https://www.rfc-editor.org/rfc/rfc8941.html#section-3.1.1

[RFC_8941_3_3_5]: https://www.rfc-editor.org/rfc/rfc8941.html#section-3.3.5

[RFC_9068]: https://www.rfc-editor.org/rfc/rfc9068.html

[RFC_9101]: https://www.rfc-editor.org/rfc/rfc9101.html

[RFC_9126]: https://www.rfc-editor.org/rfc/rfc9126.html

[RFC_9207]: https://www.rfc-editor.org/rfc/rfc9207.html

[RFC_9278]: https://www.rfc-editor.org/rfc/rfc9278.html

[RFC_9396]: https://www.rfc-editor.org/rfc/rfc9396.html

[RFC_9396_9_2]: https://www.rfc-editor.org/rfc/rfc9396.html#section-9.2

[RFC_9421]: https://www.rfc-editor.org/rfc/rfc9421.html

[RFC_9421_1_4]: https://www.rfc-editor.org/rfc/rfc9421.html#section-1.4

[RFC_9421_2_2_2]: https://www.rfc-editor.org/rfc/rfc9421.html#section-2.2.2

[RFC_9421_2_3]: https://www.rfc-editor.org/rfc/rfc9421.html#section-2.3

[RFC_9421_2_5]: https://www.rfc-editor.org/rfc/rfc9421.html#section-2.5

[RFC_9421_3_3]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3

[RFC_9421_3_3_1]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3.1

[RFC_9421_3_3_2]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3.2

[RFC_9421_3_3_3]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3.3

[RFC_9421_3_3_4]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3.4

[RFC_9421_3_3_5]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3.5

[RFC_9421_3_3_6]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3.6

[RFC_9421_3_3_7]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3.7

[RFC_9421_4_3]: https://www.rfc-editor.org/rfc/rfc9421.html#section-4.3

[RFC_9440]: https://www.rfc-editor.org/rfc/rfc9440.html

[RFC_9449]: https://www.rfc-editor.org/rfc/rfc9449.html

[RFC_9449_8]: https://www.rfc-editor.org/rfc/rfc9449.html#section-8

[RFC_9449_11_2]: https://www.rfc-editor.org/rfc/rfc9449.html#section-11.2

[RFC_9470]: https://www.rfc-editor.org/rfc/rfc9470.html

[RFC_9493]: https://www.rfc-editor.org/rfc/rfc9493.html

[RFC_9530]: https://www.rfc-editor.org/rfc/rfc9530.html

[RFC_9700]: https://www.rfc-editor.org/rfc/rfc9700.html

[RFC_9701]: https://www.rfc-editor.org/rfc/rfc9701.html

[RFC_9728]: https://www.rfc-editor.org/rfc/rfc9728.html

[OIDF]: https://openid.net/

[OIDF_ABC_WG]: https://openid.net/wg/connect/

[OIDF_AUTHZ_WG]: https://openid.net/wg/authzen/

[OIDF_DCP_WG]: https://openid.net/wg/digital-credentials-protocols/

[OIDF_EAP_WG]: https://openid.net/wg/eap/

[OIDF_IDA_WG]: https://openid.net/wg/ekyc-ida/

[OIDF_FAPI_WG]: https://openid.net/wg/fapi/

[OIDF_FASTFED_WG]: https://openid.net/wg/fastfed/

[OIDF_HEART_WG]: https://openid.net/wg/heart/

[OIDF_IGOV_WG]: https://openid.net/wg/igov/

[OIDF_MODRNA_WG]: https://openid.net/wg/modrna/

[OIDF_SS_WG]: https://openid.net/wg/sharedsignals/

[ACCOUNT_PORTING]: http://openid.net/specs/openid-connect-account-porting-1_0.html

[AUTHZEN]: https://openid.github.io/authzen/

[CAEP]: https://openid.github.io/sharedsignals/openid-caep-1_0.html

[CIBA]: https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html

[FAPI_CIBA]: https://openid.net/specs/openid-financial-api-ciba.html

[FAPI1_ADVANCED]: https://openid.net/specs/openid-financial-api-part-2-1_0-final.html

[FAPI1_BASELINE]: https://openid.net/specs/openid-financial-api-part-1-1_0-final.html

[FAPI2_ATTACKER]: https://openid.net/specs/fapi-attacker-model-2_0-final.html

[FAPI2_HTTP_SIGNATURES]: https://openid.bitbucket.io/fapi/fapi-2_0-http-signatures.html

[FAPI2_MESSAGE_SIGNING]: https://openid.bitbucket.io/fapi/fapi-2_0-message-signing.html

[FAPI2_SECURITY]: https://openid.net/specs/fapi-security-profile-2_0-final.html

[FASTFED_CORE_ID1]: https://openid.net/specs/fastfed-core-1_0-ID1.html

[FASTFED_SAML_ID1]: https://openid.net/specs/fastfed-saml-1_0-ID1.html

[FASTFED_SCIM_ID1]: https://openid.net/specs/fastfed-scim-1_0-ID1.html

[FORM_POST]: https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html

[GRANT_MANAGEMENT]: https://openid.bitbucket.io/fapi/oauth-v2-grant-management.html

[HEART_FHIR_OAUTH]: https://openid.net/specs/openid-heart-fhir-oauth2-1_0.html

[HEART_FHIR_UMA]: https://openid.net/specs/openid-heart-fhir-uma2-1_0.html

[HEART_OAUTH]: https://openid.net/specs/openid-heart-oauth2-1_0.html

[HEART_UMA]: https://openid.net/specs/openid-heart-uma2-1_0.html

[IGOV_OAUTH]: https://openid.net/specs/openid-igov-oauth2-1_0.html

[IGOV_OIDC]: https://openid.net/specs/openid-igov-openid-connect-1_0.html

[IGOV_USE_CASES]: https://openid.net/specs/openid-igov-use-cases-1_0.html

[JARM]: https://openid.net/specs/oauth-v2-jarm-final.html

[EAP_ACR_VALUES]: https://openid.net/specs/openid-connect-eap-acr-values-1_0.html

[HAIP_WG_DRAFT]: https://openid.github.io/oid4vc-haip/openid4vc-high-assurance-interoperability-profile-wg-draft.html

[MOBILE_REGISTRATION]: https://openid.net/wordpress-content/uploads/2014/04/draft-mobile-registration-01.html

[MODRNA_AUTH]: http://openid.net/specs/openid-connect-modrna-authentication-1_0.html

[MODRNA_DISCOVERY]: https://openid.net/specs/openid-connect-modrna-discovery-1_0.html

[MULTI_RES_TYPES]: https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html

[NATIVESSO]: https://openid.net/specs/openid-connect-native-sso-1_0.html

[OID4VCI]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html

[OID4VCI_ID1]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-ID1.html

[OID4VP]: https://openid.net/specs/openid-4-verifiable-presentations-1_0-final.html

[OIDC_ASC]: https://openid.bitbucket.io/ekyc/openid-connect-advanced-syntax-for-claims.html

[OIDC_AUTHORITY]: https://openid.bitbucket.io/ekyc/openid-authority.html

[OIDC_BACK_LOGOUT]: https://openid.net/specs/openid-connect-backchannel-1_0.html

[OIDC_CLAIMS]: https://openid.net/specs/openid-connect-claims-aggregation-1_0.html

[OIDC_CORE]: https://openid.net/specs/openid-connect-core-1_0.html

[OIDC_DCR]: https://openid.net/specs/openid-connect-registration-1_0.html

[OIDC_DISCOVERY]: https://openid.net/specs/openid-connect-discovery-1_0.html

[OIDC_ENTERPRISE]: https://openid.net/specs/openid-connect-enterprise-extensions-1_0.html

[OIDC_EPHEMERAL]: https://openid.net/specs/openid-connect-ephemeral-subject-identifier-1_0.html

[OIDC_FRONT_LOGOUT]: https://openid.net/specs/openid-connect-frontchannel-1_0.html

[OIDC_MIGRATION]: https://openid.net/specs/openid-connect-migration-1_0.html

[OIDC_RP_LOGOUT]: https://openid.net/specs/openid-connect-rpinitiated-1_0.html

[OIDC_SESSION]: https://openid.net/specs/openid-connect-session-1_0.html

[OIDC4IDA]: https://openid.net/specs/openid-connect-4-identity-assurance-1_0-final.html

[OIDC4IDA_CLAIMS]: https://openid.net/specs/openid-connect-4-ida-claims-1_0-final.html

[OIDFED]: https://openid.net/specs/openid-federation-1_0.html

[OIDFED_LISTING]: https://openid.net/specs/openid-federation-extended-listing-1_0.html

[OIDFED_WALLET]: https://openid.net/specs/openid-federation-wallet-1_0.html

[OPENID_ATTACHMENTS]: https://openid.net/specs/openid-connect-4-ida-attachments-1_0-final.html

[OPENID_IDA_SCHEMA]: https://openid.net/specs/openid-ida-verified-claims-1_0-final.html

[PROMPT_CREATE]: https://openid.net/specs/openid-connect-prompt-create-1_0.html

[PROVIDER_COMMANDS]: https://openid.net/specs/openid-provider-commands-1_0.html

[RISC]: https://openid.net/specs/openid-risc-profile-specification-1_0.html

[RP_META_CHOICES]: https://openid.net/specs/openid-connect-rp-metadata-choices-1_0.html

[SIOPv2]: https://openid.net/specs/openid-connect-self-issued-v2-1_0.html

[SSF]: https://openid.github.io/sharedsignals/openid-sharedsignals-framework-1_0.html

[UNMET_AUTH_REQ]: https://openid.net/specs/openid-connect-unmet-authentication-requirements-1_0.html

[USER_QUESTIONING]: http://openid.net/specs/openid-connect-user-questioning-api-1_0.html

[IANA_HTTP_FORWARDED_PARAMETERS]: https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#forwarded

[IANA_HTTP_SIGNATURE_ALGORITHMS]: https://www.iana.org/assignments/http-message-signature/http-message-signature.xhtml#signature-algorithms

[NIST_FIPS_180_4]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf

[X_690]: https://www.itu.int/rec/T-REC-X.690/

[CEDAR]: https://www.cedarpolicy.com/

[CEDAR_EXAMPLE_SCENARIO]: https://docs.cedarpolicy.com/overview/scenario.html

[CEDAR_INTEGRATIONS]: https://www.cedarpolicy.com/en/integrations

[CEDAR_JSON_FORMAT]: https://docs.cedarpolicy.com/policies/json-format.html

[CEDAR_POLICY_SET_FORMAT]: https://docs.cedarpolicy.com/policies/json-format.html#policy-set-format

[CEDAR_REFERENCE]: https://docs.cedarpolicy.com/

[HTTP_FIELD_PARSER]: https://github.com/authlete/http-field-parser

[FRONT_END_HTTPS]: https://learn.microsoft.com/en-us/previous-versions/tn-archive/aa997519\(v=exchg.65\)

[X_FORWARDED_FOR]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For

[X_FORWARDED_HOST]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host

[X_FORWARDED_PORT]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/x-forwarded-headers.html#x-forwarded-port

[X_FORWARDED_PROTO]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto

[OIDFED_ARTICLE]: ../oidcfed/

[STEPUP_AUTHN_ARTICLE]: ../stepup_authn/

[CONTACT]: ../../contact/
