Grant Management for OAuth 2.0

# はじめに

**[Grant Management for OAuth 2.0](https://openid.net/specs/fapi-grant-management.html)**（グラント管理）は、クライアントアプリケーションに付与された権限を管理するための技術仕様で、既存の標準仕様よりも細かい粒度での管理を可能にします。

**[英国オープンバンキング](https://www.openbanking.org.uk/)**、**[オーストラリア消費者データ権](https://www.cdr.gov.au/)**、**[ブラジルオープンバンキング](https://openbankingbrasil.org.br/)** 等のエコシステムで採用され、高度 API セキュリティのデファクトスタンダードとなった **Financial-grade API 1.0**、その後継仕様である **Financial-grade API 2.0** の一部と位置付けられていることが、この仕様に注目すべき理由となっています。また、グラント管理仕様は、インターネット上に世界規模の高信頼性デジタルアイデンティティネットワークを構築するためのプロジェクトである **GAIN** (**Global Assured Identity Network**) の[ホワイトペーパー](https://gainforum.org/GAINWhitePaper.pdf "GAIN DIGITAL TRUST")でも言及されています。

:::note info
本記事は <a href="https://www.authlete.com/ja">Authlete 社</a>のウェブサイト上で公開されている『<a href="https://www.authlete.com/ja/developers/grant_management/">グラント管理（Grant Management for OAuth 2.0）</a>』の仕様説明部分の要約です。当仕様の実装詳細にご興味のある方は Authlete 社の記事を参照してください。
:::

# グラントの概念

OAuth 2.0 では、ユーザーはクライアントアプリケーションに権限を与えます。結果として、認可サーバーからクライアントアプリケーションにアクセストークンが発行されます。

アクセストークン発行処理は繰り返されるかもしれません。二番目以降の処理でクライアントが要求する権限は最初の処理で要求されたものとは異なるかもしれません。このようにして、クライアントアプリケーションに与えられる権限は累積していきます。

グラント管理仕様では累積した権限を**グラント**と呼び、グラントに関する問い合わせ、グラントの累積および失効の手段を定義します。

![grant.ja.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/4d4b9872-1a66-de1e-b95d-c953afdae42a.png)

# リクエストパラメーター／レスポンスパラメーター

グラントを管理するため、各グラントには**グラント ID** が割り当てられます。グラント ID はアクセストークンとともにトークンエンドポイントから発行されます。認可サーバーにグラント ID の発行を要求するには、新しい認可リクエストパラメーターである `grant_management_action` に `create` という値を指定し、認可リクエストに含めます。発行されたグラント ID は、新しいトークンレスポンスパラメーターである `grant_id` の値としてトークンレスポンスに含まれます。

![grant_management_action-create.ja.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/dfa39668-200d-2a8a-6f3a-cc98a46b9e4f.png)

権限を累積させるには、続く認可リクエストに `grant_management_action=merge` および新しい認可リクエストパラメーター `grant_id` を含めます。`grant_id` の値には発行済みのグラント ID を指定します。

![grant_management_action-merge.ja.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/ea7c71fa-1dfc-f4cf-44ab-5c30a22d7389.png)

:::note warn
<a href="https://bitbucket.org/openid/fapi/pull-requests/344">FAPI PR 344</a> により、`update` は `merge` へと名称変更されました。この変更は Grant Management for OAuth 2.0 の実装者向け草稿第二版から有効になります。
:::

`create` と `merge` に加え、`grant_management_action` リクエストパラメーターの値として `replace` も定義されています。`replace` アクションは、指定したグラント ID に紐付く権限を、`grant_management_action=replace` を含むリクエストで付与された権限だけで置き換えます。以前の権限は取り消されます。

![grant_management_action-replace.ja.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/16cca19e-c3d4-e17f-90b0-47499182466c.png)

`grant_management_action` リクエストパラメーターの値が `merge` または `replace` の場合、`grant_id` リクエストパラメーターは必須です。

# グラント管理エンドポイント

グラント管理仕様では、**グラント管理エンドポイント**と呼ばれる新しいエンドポイントを定義しています。このエンドポイントにより、グラントに関する問い合わせとグラントの失効が可能となります。エンドポイントにアクセスするには、事前に発行されたアクセストークンが必要となります。

## 問い合わせ

|**問い合わせ**|||
|:--|:--|:--|
|リクエスト|HTTP メソッド|`GET`|
||URL|`{エンドポイント}/{グラントID}`|
||保護|`grant_management_query` スコープを持つアクセストークン|
|レスポンス|ステータスコード|200 OK|
||Content-Type|`application/json`|
||フォーマット|"[6.4. Query Status of a Grant](https://openid.net/specs/fapi-grant-management.html#name-query-status-of-a-grant)" 参照|

次のものは、グラント管理仕様の　"[6.4. Query Status of a Grant](https://openid.net/specs/fapi-grant-management.html#name-query-status-of-a-grant)" から抜粋したレスポンス例です。

![grant-status.ja.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/7dc5a2a6-2e70-a549-6a9e-6f5968aee2fa.png)

レスポンスボディーの JSON はグラントの権限を示しています。トップレベルのプロパティーとして `"scopes"`, `"claims"`, `"authorization_details"` が含まれています。

### scopes

`"scopes"` の値は、スコープとリソースの（以降スコープリソースクラスター）の配列です。各クラスターは `"scope"` と `"resource"` で構成されます。`"scope"` の値は空白区切りで並べたスコープ群です。`"resource"` の値はリソースの配列です。スコープ群とリソース群の値は `scope` リクエストパラメーター（[RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html) / [Section 3.3](https://www.rfc-editor.org/rfc/rfc6749.html#section-3.3)）と `resource` リクエストパラメーター（[RFC 8707](https://www.rfc-editor.org/rfc/rfc8707.html) / [Section 2](https://www.rfc-editor.org/rfc/rfc8707.html#name-resource-parameter)）で指定されたものです。

`"scopes"` の構造から、`merge` 操作の繰り返しにより権限が累積してもスコープリソースクラスター群は分けて管理すべきということに実装者は気が付きます。もしも `merge` 操作でクラスター群の内容が混ざってしまうと、スコープ群が意図しないリソース群と組み合わされてしまうかもしれません。下図内の右下の JSON は、クラスター群の内容を混ぜてしまうと `change` スコープが `https://payment.example.com` リソースに関連付けられてしまうことを示しており、これは、金銭の窃取などの重大なセキュリティ事件を引き起こしかねません。

![scope-resource-cluster.ja.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/16d3d7bf-005c-e910-8b81-14ce6b82f97a.png)

### claims

`"claims"` の値は、ユーザーがクライアントアプリケーションに知ることを許可した「クレーム」の配列です。簡単に言うと、ここでの「クレーム」とはユーザーの属性のことです。`family_name` や `phone_number` などのユーザー属性のいくつかは、[OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) の [Section 5.1](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) で標準クレームとして定義されています。

クライアントアプリケーションは、特別なスコープ（`profile`、`email`、`address`、`phone`）を `scope` リクエストパラメーターに含めることにより（[OIDC Core](https://openid.net/specs/openid-connect-core-1_0.html) / [Section 5.4](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims)）、または `claims` リクエストパラメーターを使うことにより（[OIDC Core](https://openid.net/specs/openid-connect-core-1_0.html) / [Section 5.5](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter)）、クレームを要求することができます。

![requesting-claims.ja.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/eaa44c77-b50f-5b2f-e753-d913aceef351.png)

### authorization_details

`"authorization_details"` の値は認可に関する詳細情報の配列です。それらの情報は、"**[OAuth 2.0 Rich Authorization Requests](https://datatracker.ietf.org/doc/draft-ietf-oauth-rar/)**"（**RAR**）で定義される `authorization_details` リクエストパラメーターで指定されたものです。

RAR が開発される前は、認可リクエストに詳細情報を含めるために認可サーバーは非標準リクエストパラメーターを導入するか、または `scope` リクエストパラメーターを非標準の方法で利用しなければなりませんでした。後者の方法は「parameterized scope」や「dynamic scope」と呼ばれ、動的な値をスコープ文字列に埋め込みます。例えば、[Open Banking Brasil Financial-grade API Security Profile 1.0](https://openbanking-brasil.github.io/specs-seguranca/open-banking-brasil-financial-api-1_ID3.html) は「動的同意スコープ」（[Section 7.1.2](https://openbanking-brasil.github.io/specs-seguranca/open-banking-brasil-financial-api-1_ID3.html#name-dynamic-consent-scope-defin)）を定義しています。そのフォーマットは `consent:{ConsentID}` となっており、`consent:` が固定文字列のプレフィックス、`{ConsentID}` が動的部分です（例: `consent:urn:bancoex:C1DD33123`）。

動的スコープの問題は、標準化されていないこと、および、それ自身に起因する特性として書式が多岐に渡ることにより、実装間の互換性に全く期待できないことです。RAR はその問題を解決するために開発され、Financial-grade API 2.0 の一部として位置付けられています。

![authorization_details.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106044/4a855da9-7f73-2b87-37bb-7857468ac690.png)

### 失効

|**失効**|||
|:--|:--|:--|
|リクエスト|HTTP メソッド|`DELETE`|
||URL|`{エンドポイント}/{グラントID}`|
||保護|`grant_management_revoke` スコープを持つアクセストークン|
|レスポンス|ステータスコード|204 No Content|

グラント管理仕様は、失効するグラントに紐付く全てのリフレッシュトークンも失効しなければならず（MUST）、アクセストークンも失効すべき（SHOULD）と述べています。

:::note warn
アクセストークンとリフレッシュトークンの実装は、認可サーバーの実装間で異なります。特に、PKI の <a href="https://en.wikipedia.org/wiki/Certificate_revocation_list">CRL</a>（Certificate Revocation List）または <a href="https://en.wikipedia.org/wiki/Online_Certificate_Status_Protocol">OCSP</a>（Online Certificate Status Protocol）相当の仕組みを運用しない限り、<b>JWT アクセストークンをはじめとする内包型アクセストークンは失効できない</b>ことに注意してください。詳細は『<a href="https://qiita.com/TakahikoKawasaki/items/970548727761f9e02bcd">OAuth アクセストークンの実装に関する考察</a>』を参照してください。
:::

# サーバーメタデータ

グラント管理仕様は次のサーバーメタデータを追加します。

|メタデータ|説明|
|:--|:--|
|`grant_management_actions_supported`|認可サーバーがサポートするグラント管理アクション群。取りうる値は `create`, `query` `replace`, `revoke` および `merge`.|
|`grant_management_endpoint`|グラント管理エンドポイントの URL|
|`grant_management_action_required`|`grant_management_action` リクエストパラメーターが常に要求されるかどうかを示す真偽値|

セキュリティ上の理由により、グラント管理はコンフィデンシャルクライアントのみしか利用できないので（_"grant management is restricted to confidential only clients due to security reasons"_（[Section 5.1](https://openid.net/specs/fapi-grant-management.html#name-requirements-for-authorizat)））、ディスカバリー文書（[OIDC Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html)）が `"grant_management_action_required":true` を含んでいる場合、それは当認可サーバーの認可エンドポイントがパブリッククライアントからのリクエストを全て拒絶することを示唆しています。

# さいごに

Grant Management for OAuth 2.0 は Authlete 2.3 以降でサポートされます。現時点では、当バージョンは共有サーバー（`api.authlete.com`）にデプロイされていません。Authlete 2.3 へのアーリーアクセスをご希望の場合は[お問い合わせ](/ja/contact/)ください。