OAuth 2.0で最近提案された"Chain" Grant Typeについて調べた

そういえば、GoogleAPIもついにOAuth 2.0(たぶんDraft 10相当)への対応が行われたとかで、だいぶ浸透してきた感があるOAuth 2.0ですが、まだ仕様はFixしていません。

というか、仕様がFixしてもその単体の仕様に全ての定義が含まれるということにはなりません。
Access Tokenの形式(Access Token Type)はOAuth 2.0本体の仕様ではなく別の仕様になりますし、Access Tokenの取得方法であるGrant Typeについても拡張が許可されています。
今回は、新しいGrant Type仕様が提案されたというので調べてみました。

まずはブログエントリについて

Independent Identity: OAuth: New Chain Grant Type

"Chain"という、OAuth 2.0のgrant typeが提案されています。
grant typeはアクセストークンを取得するときに指定するリクエストの形式って感じですね。
使いどころですが、"あるClientに対するProtected Serviceが、別のProtected ServiceのClientとして動作する場合"となります。

あまり面倒くさい印象を持たせるのは良くないので、難しそうな用語は使わないようにします。
厳密な内容を知りたければ、iKnowで英語頑張って読むか、掲示板サービスで翻訳してもらうか、海外のエンジニアに安い値段で翻訳してもらってください。

As currently specified, the OAuth framework handles only one client-service pair relationship, it cannot handle multiple 'chained' service calls.

とか言ってますが、今のOAuth 2.0の仕様で"Resource Serverからさらに別のResource Serverにアクセスする"という要件を満たしたい場合、複数のScopeが付いたAccess Tokenを使いまわすことで一応実現はできます。

まぁ、これは最初からAccess Tokenに複数のScopeをアサインしておいて並列にアクセスが行くところの一部分を直列にしただけなので、このResource Server B が Resource Server Aからのアクセスしか受けたくない場合はどうなるでしょうか?

今回の"Chain" Grant Typeではどうなるかと言うと、こんな感じです。
上の2枚の図とは比較しないでください。

いわゆる3leggedなフローの場合の例になります。
AuthZの処理は省略で、AuthZ Codeを受け取った後のいつものフローが(1),(2),(3)です。

(4)ですが、Resource Server AはResource Server BにアクセスするためのAccess Tokenを要求します。
このとき、Clientから受け取ったAccess Token 1と自分自身の特定のためのClient Credential Aを渡します。
この新しいリクエストに含まれるgrant typeが、"Chain"ということになります。

(5)でToken Server BはAccess Token 2を返し、(6)ではそれを使ってResource Server AがResource Server Bにアクセスします。
こんな感じで何段もつなげますよと言っています。

とりあえず、やりたいことはこれだけです。思ったよりも簡単な話ですね。

これで世の中が少し幸せに…なれますか?何かひっかかってるんですね。わかります。
このClient/Resource ServerA/Bの関係です。現行のOAuthだとどう実装されているのでしょうか。

例として、"独自のログイン機能を持つ写真共有サイト"と"Twitter"の関係を考えましょう。

  1. 写真共有サイトは最初にtwitterのOAuth Tokenを取得しておいて自分たちのユーザーに紐づけておく
  2. 開発者は写真共有サイトのOAuth対応された画像投稿APIを叩くと、写真共有サイト側が紐づけておいたTwitterのOAuth tokenを使ってTwitterに投稿する

この例の場合、写真共有サイトが自分たちのアカウントとtwitterアカウントの紐づけをしています。
絵描こうと思いましたがめんどくさくなったのでやめました。

これに対し、今回の"Chain" Flowは、Resource ServerA/B間の間にある程度の信頼関係があることが想定されているようです。
ブログにもこう書かれています。

The Chain Grant Profile

In this scenario, the Bank is acting under some high level of trust with the Trader service. E.g. the Bank owns the Trader service.

写真共有サイトとTwitterの間ではユーザー識別やらしっかりとAuthZをとっているかどうかなどを確認できるしくみが存在する。
よってClient-Resource A間、Resource A - Resource B間の2段でOAuthの認可処理が必要だったところが少し簡略化されます。

信頼関係なりたってるなら2leggedでいいじゃんなどと思ったりしますが、その中身まで仕様で定義できますかというと難しそうですし、信頼性のあるサーバ(ドメイン)間でユーザーのAuthZ結果を安全に伝搬していくという意味合いで"Chain" Grant Typeが考えられているようですね。
うまく説明できなくて心折れたので誰かにこのへんのフォローはまかせて仕様を紹介します。

仕様で定義されていること

気を取り直して、Draftを読みましょう。
draft-hunt-oauth-chain-00 - Chain Grant Type for OAuth2

1. Introduction

この仕様は、わかれているOAuthインフラやドメイン間でAuthZを"つなぐ"ことを可能にする拡張grant typeの定義です。
シナリオについては省略。

2. Chained OAuth token Request

図の解説については最初の方で説明したのと同じなので省略します。

2.1 Client Requests OAuth token

Access Tokenを要求するため、Token Endpointに下記のパラメータを送ります。

もちろん、Client Credentialsも必要です。
Access Tokenの有効期限は短くし、Originating Clientから受け取ったToken文字列を用いて何回も更新するようにすべきです。
また、Refresh Tokenは出すべきではありません。

2.2 Processing Requirements

Token Serverが行うOAuth TokenとClient Credentialsの処理について説明されています。
が、OAuth TokenからAuthZ情報を取得して、ちゃんと確認せーよってことぐらいしか書いてありません(しかも詳細はこの仕様外)。

2.3 Error Response

省略します。

2.4 Example (non-normative)

少しサンプルが載っていますが、わかりづらいしパラメータ足りない気もします。
そこで、Draft 13準拠のサンプルリクエスト/レスポンスを自分で考えてここに書いておきます。

Access Token Type : "bearer"

まずは、bearer token typeが使われる場合のサンプルです。
Originating ClientからDomain AのProtected Serverへのリクエストはこうなります。

GET /resource/1 HTTP/1.1
Host: resource.domaina.com
Authorization: BEARER bearertokenstring

次に、Domain AのProtected ServerがClientとしてDomain BのToken Serverに送るリクエストはこうなるでしょう。

POST /token HTTP/1.1
Host: authz.domainb.com
Content-Type: application/x-www-form-urlencoded

grant_type=http%3A%2F%2Foauth.net%2Fgrant_type%2Fchain
&oauth_token=bearertokenstring
&client_id=cidstring
&client_secret=csecretstring

レスポンスはこうなるでしょう。

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store

{
"access_token":"newbearertokenstring",
"token_type":"bearer",
"expires_in":3600
}

上でも説明されているとおり、Refresh Tokenは渡れないようです。
Access Tokenの有効期限は短く設定されると思います。
最後に、Domain AのProtected ServerがDomain BのResource Serverに送るリクエストはこうなります。

GET /resource/1 HTTP/1.1
Host: resource.domainb.com
Authorization: BEARER newbearertokenstring

Access Token Type : "MAC"

次に、MAC token typeが使われる場合のサンプルです。
Originating ClientからDomain AのProtected Serverへのリクエストはこうなります。

GET /resource/1 HTTP/1.1
Host: resource.domaina.com
Authorization: MAC token="mactokenstring",
timestamp="137131200",
nonce="noncestring",
signature="sigstring"

署名などがついています。
次に、Domain AのProtected ServerがClientとしてDomain BのToken Serverに送るリクエストはこうなるでしょう。

POST /token HTTP/1.1
Host: authz.domainb.com
Content-Type: application/x-www-form-urlencoded

grant_type=http%3A%2F%2Foauth.net%2Fgrant_type%2Fchain
&oauth_token=MAC%20token%3d%22mactokenstring%22%2ctimestamp%3d%22137131200%22%2cnonce%3d%22noncestring%22%2csignature%3d%22sigstring%22
&client_id=cidstring
&client_secret=csecretstring

oauth_tokenにはURL Encodeした値が突っ込まれます。
長いですね。レスポンスもMAC Tokenにしましょう。

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store

{
"access_token":"newmactokenstring",
"token_type":"mac",
"secret":"newmactokensecret",
"algorithm":"hmac-sha-256",
"expires_in":3600
}

最後に、Domain AのProtected ServerがDomain BのResource Serverに送るリクエストはこうなります。

GET /resource/1 HTTP/1.1
Host: resource.domainb.com
Authorization: MAC token="newmactokenstring",
timestamp="13713xxxx",
nonce="newnoncestring",
signature="newsigstring"

これで、リクエスト/レスポンスのイメージは掴めたでしょうか。

3. Security Considerations

あまり書いてありません。

3.1 Single Domain

ドメインが同一(A=B)の場合、同じToken Server(Endpoint)を使います。
同じだとしても、client credential(Clientから受け取ったAccess Token)とProtect ServerAのCredentialを紐づけるために新しいTokenを要求しても良いと書いてあります。
ふと思いつきました。例えばY!Jのように大きなところがOAUth を用いたたくさんのAPIを公開していたとして、内部からのアクセスでマッシュアップのように使いたいとき、このChain Grant Typeを使うとOAuthの仕様としてアクセス元の確認もできるので便利かもしれませんね。

3.2 Multi-Domain

ドメインが複数(A!=B)の場合、以下のような事前のすり合わせが必要です。

  • OAuth TokenがどのToken Serverで発行されるかを特定できるようにする
  • OAuth Tokenの処理方法
  • 受け取ったCredentialの紐づけ

まぁ、ここはまた後でわかりやすく更新されるでしょう。

4. IANA Considerations

この仕様では、Token Endpointへのリクエストパラメータに追加でoauth_tokenが含まれます。
このパラメータがIANAへの登録対象となります。

  • Parameter name: oauth_token
  • Parameter usage: token request
  • Change controller: IETF
  • Specification document(s): draft-hunt-oauth-chain(この仕様)

まとめ

長くなりましたが、やっていることはそれほど難しくありませんでした。
オリジナルのエントリでは銀行の例がありましたが、わかりやすい例かと言われると微妙ですね。

(長くなったので何か言いたかったことを忘れました。。。)

Chain=鎖といえば、やはり聖闘士星矢の瞬ですね。
アンドロメダ星座の瞬 - Wikipedia

ではまた。