Codin’ In The Free World

2008-08-18

[] Outbound OAuthを実現するOAuth Proxy

OAuthプロトコル上でのプレイヤー数について

三者間のやりとり

OAuthは基本的に、サービスプロバイダ、コンシューマ、エンドユーザの、

三者間でのやりとりのためのプロトコルです。(3-leggedと呼ばれます。)


二者間でのやりとり

また、サービスプロバイダとコンシューマがダイレクトにやり取りを行う

OAuth Consumer Requestという拡張仕様もあります。(こちらは2-legged

OAuthと呼ばれます。)トークンを使わずに署名だけでやり取りを行います。

元々は、コンシューマが、エンドユーザーでなく自分自身のデータを取得

したいときに利用されるためのものでしたが、OpenSocialにおいて、複雑な

やり取りを行うときに、エンドユーザーの混乱を防ぐために用いられるシーン

があります。こちらについては後日説明します。


その他

OAuthのプロトコルでは、上に挙げた、3-legged, 2-leggedのやり取りしか

行えません。ここで、iGoogleのようなガジェットコンテナを想像してみましょう。

例えばデベロッパーAさんがMySpaceのAPI(認証はOAuth)を利用して、データを

表示するiGoogleガジェットを作成したとします。そしてエンドユーザーである

Bさんは、Aさんが作成したガジェットを、自分のiGoogleページに張りつけます。

登場するプレイヤーは四者になってしまいました。

  • MySpace(データ提供元)
  • iGoogle(ガジェットコンテナ)
  • Aさんが作成したガジェット
  • Bさん

OpenSocialなんかだと、さらに、ガジェットを貼ったBさんのページを

見に来たCさんが登場したりします。

今回は、このようなケースでのOAuthの利用について解説していきます。


ガジェットコンテナでのOAuth

これらのプレイヤーを、それぞれOAuthのコンテキストで役割を考えると

MySpaceは、この場合明らかに Service Provider になります。

最終的にBさんはガジェット上で自分のMySpaceデータを閲覧することに

なるわけですから、Bさんは勿論エンドユーザーですね。

ではコンシューマの役割を果たすのは、残ったiGoogleか、Aさんが作成

したガジェットか、どちらになるでしょうか?


コンテナ内のガジェットからのOAuthの利用

まず、ガジェットからOAuthを使うときの問題点として、ガジェットというのが、

基本的にJavaScriptアプリケーションなので、ソースが見えてしまうというのがあります。

つまり、本来隠さなければならないコンシューマキーとコンシューマシークレットの

値が、エンドユーザーに見えてしまうということです。


また、クロスドメインやセキュリティ上の制約から、柔軟にAjaxで外部への

HTTPリクエストをコントロールすることも出来ません。

iGoogleやOpenSocialであればIG_Fetch*を使うなどの制約があります。


なので、直接ガジェットがOAuthの認証フローについて制御することは

出来ません。何らかの解決法を用意する必要があります。


OpenSocialでのこのようなOAuthの利用の仕方について、agektmrさんが、

Outbound OAuthとして既に説明されています。

Tender Surrender - OpenSocialのOAuthまとめ

Outbound OAuthのケース

ガジェットが外部サーバーとやり取りを行うケースですので、まずはガジェット開発者がSNSコンテナにコンシューマキーとコンシューマシークレットを登録します。ですが僕の知る限り、まだOutbound OAuthを実装しているSNSはありません。なので、ここでは何かしらの手段を用いて(SSLページでFormを使って投稿等)、コンシューマキーとコンシューマシークレットをコンテナに渡したものと想定してください。(今後順次、これを実現する方法は登場するものと思われます。)

このように、ガジェットからのOutbound OAuthを実現するために、

コンテナはOAuth Proxyとして振る舞います。


OAuth Proxy

OAuth Proxyについてはこちらで提案されています。

https://sites.google.com/site/oauthgoog/oauth-proxy

こちらのスライドを見るとイメージを掴みやすいかもしれません。

http://docs.google.com/Present?docid=dg24sz3d_974dxhcjqcj&skipauth=true

ガジェットからのOAuthの利用について、こちらでも補足されています。

http://blog.oauth.net/2008/06/04/oauth-meet-gadgets-gadgets-meet-oauth/


結論から言うと、2種類の方法が提案されてます。

現在iGoogleで既にこの手法が使えるかどうかについては未調査です。


ガジェットがコンシューマとして登録、ガジェットコンテナが代理で通信

まず、最初に挙げた例を使って説明すると、AさんがMySpace上で

コンシューマアプリケーションの登録を行い、MySpaceから、

キーとシークレット値を発行されます。


Aさんはoauthproxyreg@google.comに決められたフォーマットで

メールを送信し、コンシューマキーとコンシューマシークレットを

googleに預けます。

(agektmrさんが挙げた例のように、フォームで登録という手法もあり

ますし、この部分に関しては、Proxy提供者に実装が任されます。

iGoogleの場合は、上に挙げたURLで説明されているように、メール

で送信して登録するタイプになるようです。)


Aさんのガジェットがサービスプロバイダと通信を行いたいときは、

iGoogleのOAuth Proxyを通して通信します。


エンドユーザがこのアプリに対して承認を行うとき、Aさんが登録した

ガジェット情報が表示されることになるので(ただし認証画面での表示

に関してはプロバイダの任意)、エンドユーザは、ガジェットではない

OAuth利用アプリと同じように、承認を行えばよいことになります。


この手法の気持ち悪いところは、本来コンシューマとプロバイダ間で

しか共有され得るべきではないキーとシークレット値を、ガジェット

コンテナに渡してしまうところですね。ガジェットコンテナが悪意を

持ったサイトである場合、もしくはセキュリティ意識が低い開発者に

提供されているサービスの場合、キーとシークレットが漏れるリスクが

発生しますので、ガジェットコンテナが信頼に値するかどうか、

十分に検討が必要でしょう。


コンテナが信頼できるとしても、シークレット値を渡さなければいけない

というのは、システム的にちょっとどうかと思うところもありますが…。


ガジェットコンテナが完全にコンシューマとして振る舞う

二つ目がこちら。今回の例でいえば、あらかじめ、iGoogleが

MySpace上で、コンシューマとして登録されている必要があります。

(ここに問題があるのですが、後述します。)


AさんのガジェットがMySpaceからデータを持ってきたいとき、

iGoogleがコンシューマとして、MySpaceからデータを取得します。


勿論、これだけのやり取りでは問題が発生します。


エンドユーザーの承認が、ガジェットではなくiGoogleに対して

行われてしまいます。Aさんのガジェットに対して承認したら、

他の全てのiGoogleガジェットからも、エンドユーザーのデータ

を利用できるようになってしまいます。これはエンドユーザに

とって望むところでは無いでしょうし、承認する対象が分かりにくく、

良いユーザーエクスペリエンスとも言えません。


それを解決するための拡張がOAuth Gadget Extensionです。


iGoogleは、OAuthリクエストを送るときに、パラメータに

xoauth_app_urlとして、ガジェットのURLを加えます。

(URLは、IDとして扱えるようユニークである必要があります。)


サービスプロバイダは、iGoogleのコンシューマIDと、

このxoauth_app_urlで通知されたURLのセット一つに対して

個別に、エンドユーザーが承認できるようコントロールする

必要があります。


ただしこれでも、サービスプロバイダ上でエンドユーザーが

承認する画面で出せる、Aさんのガジェットに関する情報は、

URLだけ、という非常に少ないものになります。

もっと何かしらの工夫が必要になるかもしれませんね。


ここで、最初の iGoogle が事前にMySpaceに登録する必要がある、

という記述について、もう一度考えます。


巨大なガジェットコンテナにとって、OAuthによるAPIを提供する

たくさんのサービスに事前に登録するといったフローは、

スケールしません。


そこで、本来は事前登録して、共有鍵を発行、もしくは

公開鍵をプロバイダに登録する必要があるのですが、

そのプロセスを省いてしまおうというのが、

OAuth Key Rotation Extensionです。


リクエスト時にダイナミックに公開鍵を生成し、xoauth_pub_key

パラメータでプロバイダに渡します。


直接鍵の値を渡すのではなくて、あるエンドポイントを予め決めて、

そのエンドポイントにqueryパラメータとして渡すIDのような形に

してやることがセキュリティ上の理由で推奨されています。

詳しくは拡張仕様を参照下さい。

ただし、このあたりの処理について、サービスプロバイダがどう

振る舞えばよいかについては、記述が足りない気がしますね。


今回はOAuth Proxyについてざっくり説明しました。

次回は、ガジェットコンテナでの 2-legged OAuthの使い方について解説します。