OAuth 2.0 Grant Management
Grant Management は、ユーザーが付与した認可(Grant)を照会・管理するための API を定義した仕様です。
目次
第1部: 概要編
Grant Management とは?
Grant Management は、エンドユーザーと RP が認可サーバーで管理される認可(Grant)を照会・更新・削除できる API です。
Grant のライフサイクル:
┌────────────┐ 認可リクエスト ┌────────────┐
│ RP │ ──────────────────────► │ AS │
│ │ ◄────────────────────── │ │
└────────────┘ Grant 作成 └────────────┘
│
▼
┌─────────────┐
│ Grant │
│ - sub │
│ - client_id │
│ - scope │
│ - claims │
└────── ───────┘
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
照会(GET) 更新(PATCH) 削除(DELETE)
なぜ Grant Management が必要なのか?
背景と歴史
従来のOAuth 2.0では、ユーザーが一度認可を付与すると、その後の管理が困難でした:
| 課題 | 従来の方法 | Grant Management による解決 |
|---|---|---|
| 認可状況の確認 | ASの独自UIのみ | 標準APIで照会可能 |
| 認可の取り消し | ASの独自UIのみ | DELETE APIで取り消し |
| 段階的な認可 | 毎回新規認可 | 既存Grantを更新 |
| GDPR対応 | 個別実装 | 標準APIでユーザーに制御権 |
| RP側からの確認 | 不可能 | GET APIで状況確認可能 |
| 監査ログ | 個別実装 | 標準APIで統一的なログ |
Grant Managementは2020年頃からFAPI 2.0の一部として策定が始まり、ユーザーのプライバシーと自律性を重視した設計となっています。
脅威モデル
Grant Management が対処する主な脅威:
-
不正なGrant操作
攻撃シナリオ:
1. 攻撃者が別ユーザーのGrant IDを推測
2. 攻撃者がGrant情報を照会または削除を試みる
3. 成功すればユーザーのプライバシー侵害
対策:
- Grant IDに十分なエントロピー(128ビット以上)
- アクセス制御(subject検証)
- 監査ログ -
Grant情報の漏洩
攻撃シナリオ:
1. ネットワーク盗聴でGrant情報を取得
2. 攻撃者がユーザーの認可状況を把握
対策:
- TLS必須
- 機密情報の最小化
- レスポンスの適切なキャッシュ制御 -
GDPR違反
リスク:
- ユーザーが自分のデータを管理できない
- 忘れられる権利が実現できない
対策:
- Grant Management APIでユーザーに制御権を付与
- データエクスポート機能
- 削除の完全性保証
主要なユースケース
ユースケース1: ユーザー向けダッシュボード
シナリオ:
ユーザーが「連携アプリ管理」画面を表示
表示内容:
1. 銀行アプリ
- 権限: 口座情報の閲覧
- 付与日: 2024-01-15
- 最終利用: 2024-01-20
- [取り消し]ボタン
2. 家計簿アプリ
- 権限: 取引履歴の閲覧
- 付与日: 2024-02-01
- 最終利用: 2024-02-05
- [取り消し]ボタン
実装:
GET /grants で一覧取得
DELETE /grants/\{grant_id\} で取り消し
ユースケース2: 段階的な認可
シナリオ:
ユーザーが銀行アプリを使用中、決済機能を初めて使う
フロー:
1. 初回ログイン
scope=openid profile accounts:read
→ Grant A 作成
2. 決済機能を使う(後日)
- アプリ: 「決済権限が必要です」
- grant_id=A
- grant_management_action=merge
- scope=openid profile accounts:read payments:write
3. AS: ユーザーに追加同意を求める
「銀行アプリが決済権限を要求しています」
4. 同意後、Grant A が更新
scope=openid profile accounts:read payments:write
メリット:
- 必要な時に必要な権限のみ
- ユーザー体験の向上
- セキュリティの向上(最小権限の原則)
ユースケース3: RPによる状況確認
シナリオ:
ユーザーが「家計簿アプリで銀行口座が同期できない」と問い合わせ
RPの対応:
GET /grants/\{grant_id\}
レスポンス:
{
"grant_id": "...",
"status": "active",
"scope": "openid accounts:read"
}
→ scopeが正しいことを確認
→ 別の原因を調査
ユースケース4: GDPR対応(データポータビリティ)
シナリオ:
ユーザーが「自分のデータをエクスポートしたい」と要求
フロー:
1. GET /grants でユーザーのすべてのGrantを取得
2. 各Grantの詳細情報をJSON形式でエクスポート
3. ユーザーにダウンロード提供
データ例:
{
"user_id": "user-123",
"export_date": "2024-01-20T10:00:00Z",
"grants": [
{
"grant_id": "PTRWWMo_YsGxl17r6MBj5",
"client_name": "Banking App",
"scope": "openid accounts:read",
"created_at": "2024-01-01T00:00:00Z"
}
]
}
規制対応
GDPR(一般データ保護規則)
| GDPR要件 | Grant Management による実現 |
|---|---|
| アクセス権 | GET /grants でユーザーが自分のGrantを照会 |
| 訂正権 | PATCH /grants でGrantを更新(scope変更) |
| 削除権(忘れられる権利) | DELETE /grants でGrantを削除 |
| データポータビリティ権 | GET /grants でデータをエクスポート |
| 透明性 | Grantの詳細情報(scope, claims等)を提供 |
その他の規制
| 規制 | 地域 | Grant Management の役割 |
|---|---|---|
| CCPA | カリフォルニア州 | ユーザーのデータアクセス権・削除権を保証 |
| LGPD | ブラジル | GDPRと同様の権利を実現 |
| POPIA | 南アフリカ | データ主体の権利を実現 |
第2部: 詳細編
Grant ID
Grant はユニークな識別子(Grant ID)で識別されます。
トークンレスポンスに Grant ID が含まれる:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
"grant_id": "PTRWWMo_YsGxl17r6MBj5"
}
Grant ID の要件:
- ユニークであること: 全システムで一意
- 推測不可能であること: 128ビット以上のエントロピー
- URL Safe であること: Base64URL エンコード
- 長期間有効: Grantの有効期限まで(通常6ヶ月〜1年)
- 不変であること: 一度発行されたら変更されない
Grant ID の生成例:
Grant の照会(GET)
単一 Grant の取得
リクエスト:
GET /grants/PTRWWMo_YsGxl17r6MBj5 HTTP/1.1
Host: auth.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
レスポンス:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"grant_id": "PTRWWMo_YsGxl17r6MBj5",
"status": "active",
"client_id": "s6BhdRkqt3",
"client_name": "Banking App",
"client_uri": "https://bank.example.com",
"client_logo_uri": "https://bank.example.com/logo.png",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-15T00:00:00Z",
"expires_at": "2024-07-01T00:00:00Z",
"last_used_at": "2024-01-20T10:30:00Z",
"scope": "openid profile email accounts:read",
"claims": {
"userinfo": {
"name": null,
"email": {
"essential": true
}
}
},
"authorization_details": [
{
"type": "account_information",
"actions": ["read"],
"accounts": [
{
"iban": "DE89370400440532013000"
}
]
}
]
}
フィールドの詳細:
| フィールド | 必須 | 説明 | 型 |
|---|---|---|---|
grant_id | ✅ | Grant の一意識別子 | String |
status | ✅ | Grant の状態 | active, revoked, expired |
client_id | ✅ | クライアント ID | String |
client_name | △ | クライアント名(表示用) | String |
client_uri | △ | クライアントのURL | String (URI) |
client_logo_uri | △ | クライアントのロゴ | String (URI) |
created_at | ✅ | Grant 作成日時 | String (ISO 8601) |
updated_at | ✅ | Grant 更新日時 | String (ISO 8601) |
expires_at | △ | Grant 有効期限 | String (ISO 8601) |
last_used_at | △ | 最終利用日時 | String (ISO 8601) |
scope | ✅ | 付与されたスコープ | String (space-separated) |
claims | △ | 付与されたクレーム | Object |
authorization_details | △ | RAR(Rich Authorization Requests) | Array |
Grant 一覧の取得
リクエスト:
GET /grants?status=active&limit=10&offset=0 HTTP/1.1
Host: auth.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
レスポンス:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"grants": [
{
"grant_id": "PTRWWMo_YsGxl17r6MBj5",
"client_id": "s6BhdRkqt3",
"client_name": "Banking App",
"client_uri": "https://bank.example.com",
"client_logo_uri": "https://bank.example.com/logo.png",
"status": "active",
"scope": "openid profile email accounts:read",
"created_at": "2024-01-01T00:00:00Z",
"last_used_at": "2024-01-20T10:30:00Z"
},
{
"grant_id": "abc123def456",
"client_id": "another_client",
"client_name": "Payment App",
"client_uri": "https://payment.example.com",
"client_logo_uri": "https://payment.example.com/logo.png",
"status": "active",
"scope": "openid payment",
"created_at": "2024-01-10T00:00:00Z",
"last_used_at": "2024-01-19T15:45:00Z"
}
],
"total": 2,
"offset": 0,
"limit": 10
}
クエリパラメータ:
| パラメータ | 説明 | デフォルト | 例 |
|---|---|---|---|
client_id | 特定クライアントのGrantのみ | なし | s6BhdRkqt3 |
status | active, revoked, expired | active | active |
offset | ページネーション(開始位置) | 0 | 0 |
limit | 取得件数 | 10 | 10 |
sort | ソート順 | created_at:desc | last_used_at:desc |
ソートオプション:
created_at:asc/created_at:desc- 作成日時順updated_at:asc/updated_at:desc- 更新日時順last_used_at:asc/last_used_at:desc- 最終利用日時順expires_at:asc/expires_at:desc- 有効期限順
Grant の更新(認可の拡張)
既存の Grant に新しいスコープを追加する場合、PAR で grant_id と grant_management_action を指定します。
リクエスト:
POST /par HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
grant_id=PTRWWMo_YsGxl17r6MBj5
&grant_management_action=merge
&scope=openid profile email accounts:read payments:write
&client_id=s6BhdRkqt3
&redirect_uri=https://client.example.com/callback
&state=af0ifjsldkj
&nonce=n-0S6_WzA2Mj
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion=eyJhbGciOiJQUzI1NiIs...
Grant Management アクション:
| アクション | 説明 | 用途 | 既存Grantの扱い |
|---|---|---|---|
create | 新しい Grant を作成(デフォルト) | 初回認可 | 無視(新規作成) |
replace | 既存の Grant を置換 | 権限の完全な変更 | 完全に上書き |
merge | 既存の Grant に追加 | 段階的な認可拡張 | スコープを追加 |
create(新規作成)
動作:
- 既存のGrantは無視
- 常に新しいGrant IDを生成
- 新しいGrantを作成
使用例:
- 初回認可
- 完全に別の認可が必要な場合
replace(置換)
動作:
- 既存のGrantを完全に上書き
- Grant IDは変わらない
- scopeを完全に置き換え
使用例:
- 権限を減らす場合
- 権限を完全に変更する場合
例:
既存: scope=openid profile accounts:read payments:write
新規: scope=openid profile accounts:read
結果: scope=openid profile accounts:read
(payments:writeが削除される)
merge(マージ)
動作:
- 既存のGrantにスコープを追加
- Grant IDは変わらない
- scopeは和集合
使用例:
- 段階的な権限追加
- 機能追加時の権限拡張
例:
既存: scope=openid profile accounts:read
新規: scope=openid profile accounts:read payments:write
結果: scope=openid profile accounts:read payments:write
(payments:writeが追加される)
Grant の削除(DELETE)
リクエスト:
DELETE /grants/PTRWWMo_YsGxl17r6MBj5 HTTP/1.1
Host: auth.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
レスポンス:
HTTP/1.1 204 No Content
削除の効果(カスケード削除):
Grant削除時の影響範囲:
1. Grantの状態変更
status: active → revoked
2. アクセストークンの無効化
- 発行済みのすべてのアクセストークンが即座に無効化
- APIアクセス時に401エラー
3. リフレッシュトークンの無効化
- 発行済みのすべてのリフレッシュトークンが無効化
- トークン更新時に400エラー(invalid_grant)
4. 認可コードの無効化(未使用の場合)
- 未使用の認可コードが無効化
- トークン取得時に400エラー
5. セッションの無効化(オプション)
- SSOセッションも無効化する実装もある
注意事項:
削除の不可逆性:
- 削除は即座に反映される
- 元に戻すことはできない
- 再度認可が必要な場合は、新規の認可フローを実行
監査ログ:
- 削除操作は監査ログに記録すべき
- 誰が、いつ、どのGrantを削除したか
通知:
- ユーザーへの通知(メール等)を検討
- RPへの通知(webhook等)を検討
Grant のライフサイクル
Grantのライフサイクル:
┌──────────┐
│ 作成 │ ← 認可フロー完了時
└────┬─────┘
│
▼
┌──────────┐
│ active │ ← 通常の状態
└────┬─────┘
│
├──────────────┐
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ expired │ │ revoked │
└──────────┘ └──────────┘
│ │
└──────┬───────┘
▼
(完全削除)
状態の詳細:
| 状態 | 説明 | トークン発行 | API アクセス | 削除可能 |
|---|---|---|---|---|
active | 有効 | ✅ | ✅ | ✅ |
expired | 期限切れ | ❌ | ❌ | ✅ |
revoked | 取り消し済み | ❌ | ❌ | ✅(論理削除済み) |
ディスカバリーメタデータ
メタデータフィールド:
| フィールド | 説明 |
|---|---|
grant_management_endpoint | Grant Management API のベース URL |
grant_management_actions_supported | サポートされるアクション(create, replace, merge) |
grant_management_grant_types_supported | Grant Management対象のgrant_type |