リソース単位のアクセストークン
このドキュメントの目的
アクセストークンをAPI単位で絞り込み、リソースサーバーが自分宛てのトークンだけを受け入れられるようにすることが目標です。
学べること
✅ リソース識別子の基礎
audがリソースサーバーを指す理由- スコープとリソースの対応づけ
✅ 実践的な知識
- Management APIでの設定方法
- 段階的な導入と差し戻し
所要時間
⏱️ 約10分
前提条件
- how-to-02でテナント作成完了
- アクセストークンをJWT形式で発行していること(
access_token_type: "JWT")
なぜ必要か
JWTアクセストークンをリソースサーバーが自前で検証する場合、署名を確かめるだけでは足りません。そのトークンが自分宛てかを確認する必要があります。
複数のAPIを持つサービスで、この確認が無いとこうなります。
決済API向けに発行したトークンを、クライアントが管理APIに提示
→ 署名は正しい(同じ認可サーバーが発行したもの)
→ 管理APIが受け入れてしまう
audクレームはリソースサーバーの識別子を運びます。管理APIが「audが自分の識別子でなければ拒否する」を実装できて初めて、上の経路が塞がります。
audに入るのはリソースサーバーであってクライアントではありません。クライアントはclient_idクレームが表します。IDトークンのaudがクライアントを指すのとは役割が逆です。
設定
authorization_server.extensionに2つの項目を設定します。
| 設定項目 | 説明 |
|---|---|
scope_resource_mapping | リソース識別子と、そのリソースに属するスコープの 対応 |
default_resource_indicator | どのリソースにも対応づかないときに使う既定値 |
{
"extension": {
"access_token_type": "JWT",
"scope_resource_mapping": {
"https://api.example.com": ["account", "profile"],
"https://payments.example.com": ["payments"]
},
"default_resource_indicator": "https://api.example.com"
}
}
この設定で発行されるトークンはこうなります。
| 要求スコープ | aud |
|---|---|
account | https://api.example.com |
payments | https://payments.example.com |
account payments | invalid_scopeで拒否 |
| どれにも対応づかないスコープのみ | https://api.example.com(既定値) |
リソース識別子の書き方
キーは絶対URIでなければならず、フラグメント(#)を含められません(RFC 8707 §2)。満たさない値は無視され、警告ログが出ます。
"scope_resource_mapping": {
"https://api.example.com": ["account"], // OK
"my-api": ["account"], // 無視される(URIでない)
"https://api.example.com/#v1": ["admin"] // 無視される(フラグメント)
}
クライアントIDを書いても採用されません。audがリソースではなくクライアントを指してしまう誤りを避けるためです。
プロトコルスコープは対応づけない
openidのように全リクエストに付くスコープは、リソースに対応づけないでください。対応づけるとそのリソースが全リクエストの当事者になり、他のリソースを併せて要求した瞬間に必ず拒否されます。
// NG: openid を含む要求が payments を併せて要求できなくなる
"https://api.example.com": ["openid", "account"]
// OK
"https://api.example.com": ["account"]
複数リソースへの要求が拒否される理由
1つのアクセストークンは1つのリソースしか名乗れません。複数を名乗るトークンは、それぞれのリソースが他所向けに発行されたスコープを受け入れることを許してしまいます。
拒否はスコープが決まる場所で行われます。
| スコープが決まる場所 | 返り方 |
|---|---|
| 認可リクエスト | リダイレクトでのinvalid_scope |
| CIBAバックチャネル認証リクエスト | レスポンスボディのinvalid_scope(リダイレクト先が無いため) |
トークンリクエスト(client_credentials / password / jwt-bearer) | レスポンスボディのinvalid_scope |
リフレッシュでは検証しません。付与済みのグラントのスコープは変えられないため、設定変更を理由に既発行トークンのリフレッシュを失敗させないためです。
段階的な導入
設定はオプトインです。scope_resource_mappingが空のテナントでは検証自体が走らず、audにはissuerが入ります。
認可サーバー設定の更新は全置換です。マージされません。extensionだけを送ると、他の必須項目が欠けて400になります。
必ずGETで現在の設定を取得し、そこに項目を足して全体をPUTしてください。
1. 現在の設定を取得する
curl -sS -X GET \
https://api.local.test/v1/management/tenants/{tenant-id}/authorization-server \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-o authorization-server.json