メインコンテンツまでスキップ

RFC 8693: OAuth 2.0 トークン交換

RFC 8693 は、あるトークンを別のトークンに交換するためのプロトコルを定義した仕様です。委任(Delegation)や偽装(Impersonation)のシナリオで使用されます。


第1部: 概要編

トークン交換とは?

トークン交換は、既存のトークンを使用して別のトークンを取得する仕組みです。

基本的なフロー:

┌──────────┐ ┌──────────────┐
│ クライアント │ ── subject_token ─────────► │ 認可サーバー │
│ │ (交換したいトークン) │ │
│ │ ◄──────────────────────── │ │
└──────────┘ 新しいトークン └──────────────┘

なぜトークン交換が必要なのか?

シナリオ説明
マイクロサービスサービス間でトークンを伝播・変換
委任(Delegation)「〇〇の代理で」アクセスする
偽装(Impersonation)「〇〇として」アクセスする
トークンのダウングレードスコープを絞ったトークンを取得
クロスドメイン異なるドメイン間でトークンを交換

委任 vs 偽装

委任(Delegation):
「User A の代理で Service B がアクセス」

┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ User A │ ─► │Service │ ─► │Service │ ─► │Resource│
│ │ │ B │ │ C │ │ │
└────────┘ └────────┘ └────────┘ └────────┘

└── Token には User A と Service B の両方が記載

act (actor): Service B
sub (subject): User A

偽装(Impersonation):
「Service B が User A になりすまして」アクセス

┌────────┐ ┌────────┐ ┌────────┐
│Service │ ─► │Service │ ─► │Resource│
│ B │ │ C │ │ │
└────────┘ └────────┘ └────────┘

└── Token には User A のみ記載(Service B は見えない)

sub: User A
(act なし)

第2部: 詳細編

トークン交換リクエスト

POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&requested_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=https://api.example.com
&scope=read write

リクエストパラメータ

パラメータ必須説明
grant_typeurn:ietf:params:oauth:grant-type:token-exchange
subject_token交換したいトークン(主体を表す)
subject_token_typesubject_token のタイプ
actor_tokenアクター(代理者)のトークン
actor_token_typeactor_token のタイプ(actor_token がある場合必須)
requested_token_type要求するトークンタイプ
audienceトークンの対象者
scope要求するスコープ
resourceリソースサーバー(RFC 8707)

トークンタイプ URI

URI説明
urn:ietf:params:oauth:token-type:access_tokenアクセストークン
urn:ietf:params:oauth:token-type:refresh_tokenリフレッシュトークン
urn:ietf:params:oauth:token-type:id_tokenID トークン
urn:ietf:params:oauth:token-type:saml1SAML 1.1 アサーション
urn:ietf:params:oauth:token-type:saml2SAML 2.0 アサーション
urn:ietf:params:oauth:token-type:jwtJWT

レスポンス

HTTP/1.1 200 OK
Content-Type: application/json

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read write"
}
フィールド説明
access_token発行されたトークン
issued_token_type発行されたトークンのタイプ
token_typeトークンの使用方法(通常 Bearer
expires_in有効期限(秒)
scope付与されたスコープ
refresh_tokenリフレッシュトークン(オプション)

ユースケース別の例

1. マイクロサービス間のトークン伝播

シナリオ:
User → API Gateway → Service A → Service B → Database

API Gateway が受け取ったトークンを Service A 用に交換:

POST /token
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token={user_access_token}
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=service-a
&scope=service-a:read

2. 委任トークンの取得

シナリオ:
Service B が User A の代理で Service C にアクセス

POST /token
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token={user_a_access_token}
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&actor_token={service_b_access_token}
&actor_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=service-c

発行されるトークン(JWT):
{
"sub": "user-a",
"aud": "service-c",
"act": {
"sub": "service-b"
}
}

3. 偽装トークンの取得

シナリオ:
Admin が User A として操作

POST /token
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token={user_a_id_token}
&subject_token_type=urn:ietf:params:oauth:token-type:id_token
&requested_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=target-resource

発行されるトークン:
{
"sub": "user-a",
"aud": "target-resource"
// act クレームなし = 偽装
}

4. スコープのダウングレード

シナリオ:
広いスコープのトークンから、限定されたスコープのトークンを取得

POST /token
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token={broad_scope_token}
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&scope=read // 元のトークンより狭いスコープ

5. SAML → OAuth 変換

シナリオ:
SAML アサーションを OAuth アクセストークンに変換

POST /token
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token={base64_encoded_saml_assertion}
&subject_token_type=urn:ietf:params:oauth:token-type:saml2
&requested_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=https://api.example.com

委任チェーン

複数の委任が連鎖する場合、act クレームがネストします。

{
"sub": "user-a",
"act": {
"sub": "service-b",
"act": {
"sub": "service-c"
}
}
}
意味:
Service C が Service B を通じて User A の代理でアクセス

User A → Service B → Service C → Resource
(delegate) (delegate)

認可サーバーの検証

トークン交換リクエストの検証フロー:

1. クライアント認証
└── リクエスト元のクライアントを認証

2. subject_token の検証
├── 署名の検証
├── 有効期限の確認
├── 発行者の確認
└── 主体の識別

3. actor_token の検証(あれば)
├── 署名の検証
├── 有効期限の確認
└── アクターの識別

4. 交換の認可
├── クライアントは交換を許可されているか
├── 要求された audience は許可されているか
├── 要求された scope は許可されているか
└── 委任/偽装のポリシーに適合するか

5. 新しいトークンの発行
├── sub: subject_token の主体
├── act: actor_token の主体(委任の場合)
├── aud: 要求された audience
└── scope: 付与されたスコープ

セキュリティ考慮事項

項目推奨事項
クライアント認証交換を許可されたクライアントのみ認証
交換ポリシーどのトークンをどのように交換できるか制限
偽装の制限偽装は特権クライアントのみに許可
委任チェーンの深さネストの深さを制限
スコープの制限元のトークンより広いスコープを許可しない
監査ログすべての交換を記録
audience の検証許可された audience のみ受け入れ

エラーコード

エラー説明
invalid_requestリクエストが不正
invalid_clientクライアント認証失敗
invalid_grantsubject_token または actor_token が無効
unauthorized_client交換が許可されていない
unsupported_token_typeサポートされていないトークンタイプ
invalid_targetaudience または resource が無効

参考リンク