10月 20 2008

Orkutアプリのソースコードを覗くブックマークレット

Published by Eiji under Service

OpenSocial-Japanのメーリングリストで出した小ネタを貼っておきます。

OpenSocialアプリ(ガジェット)はiframe内に表示されているのですが、iframeタグのsrc属性に含まれるURLのquery部分に、実はソースコードとなるXMLのURLが書いてあります。 このブックマークレットを使えば、ワンクリックでソースコードを開くことができます。

Open Orkut Gadget XML

↑上記をブックマークレットとして保存し、Orkutアプリを表示した状態でクリックすると、ソースコードのページが新規ウィンドウで開きます。 (複数表示されている場合は、一番上のアプリのソースコードが開きます)

※Firefoxでしか動作確認してないので、動かなかったら教えてください。

View Comments add to hatena hatena.comment (1) add to del.icio.us (0) add to livedoor.clip (1) add to Yahoo!Bookmark (0) Total: 2

10月 04 2008

OAuthの署名方式を掘り下げる

Published by Eiji under OAuth

当ブログでこれまで何度かOpenSocialに絡んだOAuthについて取り上げてきましたが、MySpaceを参考にしていたため、署名方式としてHMAC-SHA1のみを対象にしてきました。しかしShindigを掘り下げる上でRSA-SHA1を避けて通ることはできず、むしろこちらについても十分な知識を得ていないとなかなか先に進めないことが分かりましたので、この機会にまとめてみます。(OpenSocialをある程度前提にしていますが、署名の話はOpenSocialに限らないものです。)

署名とは何か

ITの世界で署名とは、問い合わせ元がその人であることを証明するための手段、と言えます。OAuthだと、コンシューマがサービスプロバイダに対して、名乗っている通りの者であることを証明することを意味します。これは、「自分」もしくは「相手と自分」にしか分からないものをリクエストに付け加えて送ることで実現されます。

OAuthの仕様では、署名方式について厳密に規定していませんが、HMAC-SHA1、RSA-SHA1、PLAINTEXTの3つの署名方式を説明しています。

OAuth does not mandate a particular signature method, as each implementation can have its own unique requirements. The protocol defines three signature methods: HMAC-SHA1RSA-SHA1, and PLAINTEXT, but Service Providers are free to implement and document their own methods. Recommending any particular method is beyond the scope of this specification.

HMAC-SHA1

HMAC-SHA1を使ったOAuthでは、予めコンシューマとサービスプロバイダが、同じコンシューマキー(oauth_consumer_key)とコンシューマシークレット(oauth_consumer_secret)を持ちます。コンシューマシークレットはもちろん、秘密というくらいですから、2者以外に知られてはいけません。2者がコンシューマシークレットを共有することから、Shared Secretと呼んだりもします。

MySpaceでは、コンシューマキーをアプリケーションのURL、コンシューマシークレットをMD5らしきランダムな(?)ハッシュ文字列としていますが、コンシューマキーはディベロッパの任意で変更可能です。詳しくはこの辺りをご覧下さい。

RSA-SHA1

逆に、RSA-SHA1の方式では、コンシューマが公開鍵と秘密鍵を持ちますが、サービスプロバイダは秘密鍵を知ることはありません。コンシューマは秘密鍵で暗号化した署名を加えたリクエストを投げます。この暗号化された署名は、公開鍵でしか解くことができませんし、秘密鍵でしか作ることができないため、コンシューマが身分を証明できる、という訳です。

署名を作る際、HMAC-SHA1方式の場合、コンシューマシークレット(oauth_consumer_secret)とトークンシークレット(oauth_token_secret)を”&”で繋いだものをkeyとして利用しますが、RSA-SHA1方式の場合、秘密鍵をkeyとして使って暗号化します。そのため、コンシューマシークレットとトークンシークレットは不要です。

逆にサービスプロバイダは、公開鍵を使って署名が正当なものであることを確認します。

$publickeyid = openssl_get_publickey($cert);
$ok = openssl_verify($raw, $signature, $publickeyid);
openssl_free_key($publickeyid);

$certは公開鍵、$rawは署名の基本文字列(Signature Base String)、$signatureは署名文字列(oauth_signature)を表しています。$rawと$signatureはコンシューマからのリクエストで生成することができますが、$certについてはちょっと考察が必要です。

RSA-SHA1の公開鍵の扱い

OAuthの拡張としてOAuth Key Rotation Extensionが提案されています。これはコンシューマがサービスプロバイダにリクエストする際、公開鍵のIDをリクエストと一緒に渡すことで、サービスプロバイダに鍵をダウンロード/認識させるための仕様です。公開鍵のIDはxoauth_signature_publickeyパラメータで渡されます

OAuth Key Rotation Extensionのドラフト、OpenSocial/Gadgetの仕様書など、いくつかのドキュメントにxoauth_public_keyと記述されていますが、Shindigの実装でxoauth_signature_publickeyが使用されており、こちらが正式なものとなるようです。

ここで公開鍵のIDと言いましたが、もちろん、これだけでは公開鍵を取得することができないので、これを使ってゴニョゴニョする必要があります。

が、、、。

OpenSocial/Gadgetの仕様書には

The container should make its public key available for download at a well-known location. The location https://container-hostname/opensocial/certificates/xoauth_public_keyvalue is recommended.

と書いてあるのですが、Shindigの実装ではhttp://container-hostname/public.cerになっていたりと、仕様が一貫していません。

現実はどうしているかというと、xoauth_signature_publickeyを無視して、コンテナのドキュメントに書いてある公開鍵をコピペしてソースコードにハードコーディングしています。hi5、Orkutについては動作確認ができました。iGoogleについてはここに公開鍵が書いてありますが、動作しませんでした。

OAuthが広まって様々なコンシューマが登場するまでは、まだまだこの中途半端な状態が続くのではないでしょうか。

コンシューマは誰か

ここで勘のいい方は気付かれたと思いますが、RSA-SHA1方式の場合、署名元がOpenSocialのコンテナサイトそのものになっています。MySpaceのように、ガジェットではありません。ということは、もちろんコンシューマキー(oauth_consumer_key)もOrkutなら”orkut.com”、hi5なら”hi5.com”といった具合に、コンテナサイトを表すものが使用されます。

またこの方式の場合、どのガジェットがリクエストを投げているのかを表すため、xoauth_app_urlというパラメータが追加されます。これを提案しているのがOAuth Gadget Extensionです。

MySpaceのようにHMAC-SHA1を使っている場合は、ガジェットごとにコンシューマキーを設定し、ガジェットのリクエストをコンテナがProxyするという形を取っていました。これはShindigを使っているiGoogle、Orkut、hi5と、独自にOpenSocialを実装しているMySpaceとの方針の違いから来るものです。

HMAC-SHA1方式でコンテナをコンシューマとして扱おうとすると、シークレットは2者間のみで共有されなければならないため、コンシューマは、サービスプロバイダごとにキーとシークレットを発行しなければなりません。しかし、RSA-SHA1方式であれば、コンテナがコンシューマでも、公開鍵と秘密鍵の組み合わせは一つだけあれば使い回せるため、OpenSocialにおけるmakeRequest (Outbound OAuth)のように、コンテナが外部サービスからデータを取得するアーキテクチャの場合、RSA-SHA1方式にした方がコンシューマにとってサービスプロバイダの追加も容易になりますし、サービスプロバイダにとってコンシューマの署名を確認する作業も楽になるのが利点です。

ShindigがRSA-SHA1方式を中心に実装されているのはそんな理由がありそうです。ちなみに、Shindigの開発はGoogleが中心になって行われており、HMAC-SHA1方式についても現在実装中らしいですが、iGoogleでのコンシューマキーとコンシューマシークレットの発行は、メールで依頼することになっているため、今のところ本気で考えてはいなさそうです。

In the case of the iGoogle sandbox, you can send mail to oauthproxyreg@google.com with the following information to register your shared secret:
* URL of your gadget
* The shared secret assigned to you by the service provider
* The consumer key assigned to you by the service provider
* Whether to use symmetric or asymmetric signing with the service provider (or say that you don’t know)
Until your shared secret has been registered, your gadget will not work.  If you change the URL of your gadget, you will need to re-register the secret for that gadget.

まとめ

現時点ではOpenSocialのOutbound OAuthではMySpaceがHMAC-SHA1方式でガジェットをコンシューマに、Shindig系コンテナはRSA-SHA1方式でコンテナをコンシューマにしていますので、外部サーバーとやり取りを多なうOpenSocialガジェットを作る場合、どちらからのリクエストも受け付けられるよう構築しておく必要がありそうです。

参考サイト

View Comments add to hatena hatena.comment (6) add to del.icio.us (0) add to livedoor.clip (6) add to Yahoo!Bookmark (1) Total: 13

4月 17 2008

OpenSocialアプリケーションを作る(2)

Published by Eiji under OpenSocial, Widget

OpenSocialアプリケーションを作る(1)では、ガジェットの仕組みと、Orkutでアカウントを取得するところまで書きました。今回は、前回紹介したアプリケーションのコードを解説します。

このアプリケーション(FriendIntroducer)は、自分が見た場合は友達の紹介文を書くことができ、他人が見た場合はその人に向けて書かれた紹介文を読むことができる、というmixiなどにもよくある簡単なアプリケーションです。JavaScriptやjQuery的にはもっと賢い実装方法があると思いますが、今回はOpenSocialのコードにフォーカスしますので、アホなコードは大目に見てください。

ガジェットXML

< ?xml version="1.0" encoding="UTF-8" ?>
<module>
<moduleprefs title="Friend Introducer" title_url="" description="Introduce your friend!" height="100">
  <require feature="opensocial-0.7" />
  <require feature="views" />
  <require feature="dynamic-height" />
 </moduleprefs>
<content type="html" view="canvas">
  < ![CDATA[
  <link href="http://devlab.agektmr.com/OpenSocial/css/FriendIntroducer.css" rel="stylesheet" type="text/css">
  <script type="text/javascript" src="http://devlab.agektmr.com/OpenSocial/js/jquery.js"></script>
  <script type="text/javascript" src="http://devlab.agektmr.com/OpenSocial/js/FriendIntroducer.js">< /script>
  </script><script type="text/javascript">
    gadgets.util.registerOnLoadHandler(FriendIntroducer.init);
  </script>
  <div id="title"></div>
  <div id="friends"></div>
  <div id="message"></div>
  ]]>
 </content>
</module>

ここでは

  • ガジェットの設定
  • 外部のCSSやJavaScriptを読み込み
  • 初期化スクリプトの呼び出し
  • 表示用DIV指定

を行っています。

<content type="html" view="profile">

Contentはhtmlタイプ、profileビューと指定しました。typeにはhtmlとurlが選択可能ですが、htmlとして内容をContentタグで囲まれた部分に記述しています。viewはOpenSocialの仕様上profile、canvasが想定されていますが、コンテナによってhomeやpreviewが存在するようです。ここでは例としてcanvasを使用しています。

また、viewを指定しない場合はdefaultビューとして扱われます。コンテナは表示場面(コンテキスト)によってビューを切り替えますが、Content内でviewを取り出してJavaScriptで処理を分ける方法もあります。

Contentの内容

Contentの内容は、基本的に通常のウェブページと同じように扱うことができ、HTMLで書くことができますが、

  <script type="text/javascript">
    gadgets.util.registerOnLoadHandler(FriendIntroducer.init);
  </script>

このようにgadgets.util.registerOnLoadHandlerを使って初期化処理を入れることができます。

このアプリケーションでは、表示テンプレートとして空のdivタグを3つ用意しています。

JavaScriptのコード

JavaScriptのソースコードはここにありますが、抜粋して紹介します。

    $('#friends').html('Requesting friends...');
    var req = opensocial.newDataRequest();
    req.add(req.newFetchPersonRequest('VIEWER'), 'viewer');
    req.add(req.newFetchPeopleRequest('VIEWER_FRIENDS'), 'friends');
    req.add(req.newFetchPersonAppDataRequest('VIEWER', 'Introduction'), 'intro');
    req.send(FriendIntroducer.onLoadViewerFriends);

最も基本的な処理となる、閲覧者、閲覧者の友達、保存したデータを取り出す処理です。

opensocial.newDataRequest()でデータリクエストオブジェクトを作り、addで3種類のリクエストを追加、最後にsendでコールバック関数を指定した上、データリクエストを送信しています。3種類のリクエストにはそれぞれ後で区別するためviewer, friends, introという名前(キー)を付けています。

   var viewer    = response.get('viewer').getData();
   var friends   = response.get('friends').getData();
   var intro     = response.get('intro').getData();

コールバック関数では、引数(response)を使って、response.get(キー名).getData()でリクエストしていたデータを取り出すことができます。

    var viewer_id = viewer.getId();
    var json = null;
    if (intro[viewer_id]) {
      if (intro[viewer_id].Introduction) {
        var json_str = gadgets.util.unescapeString(intro[viewer_id].Introduction);
        var json = eval(json_str)[0];
      }
    }

introは、このアプリケーションを使ってコンテナのデータ保存領域に予め保存しておいた内容、つまり「以前保存した友達の紹介文」です。

    $('#title').html('<p>Friends of '+viewer.getDisplayName()+':</p>');
    var html = '';
    if (friends.size() == 0) {
      $('#message').html("<p>You don't have any friends yet!</p>");
    }

友達が誰もいない場合を考慮して、メッセージを表示しています。

    friends.each(function(person) {
      var t = FriendIntroducer.template.friend_list_canvas;
      t = t.replace('##thumbnail_url##', person.getField(opensocial.Person.Field.THUMBNAIL_URL));
      t = t.replace('##profile_url##',   person.getField(opensocial.Person.Field.PROFILE_URL));
      t = t.replace('##display_name##',  person.getDisplayName());
      t = t.replace('##input_id##',      'input_'+person.getId());
      if (json) {
        t = t.replace('##intro_text##',  json[person.getId()] ? json[person.getId()] : '');
      } else {
        t = t.replace('##intro_text##', '');
      }
      html += t;
    });
    $('#friends').html('<ul>'+html+'</ul>');

OpenSocialでは配列をなめる、いわゆるiterationも仕様に含まれていて、eachを使うことができます。ここでは、友達のリストをループして、友達の名前やサムネイル画像、保存されていた紹介文をHTMLテンプレートに埋め込んでいきます。

ここまでで、友達の紹介文を書き込むことができるcanvasページの表示することができました。次に、友達の紹介文をユーザーが書き込んだものと想定し、投稿して保存するところまでを解説します。

データの保存

OpenSocialはコンテナにデータ保存領域を持っており、アプリケーションがデータを保存することができます。これはパーシステントデータ(Persistant data)や、アプリケーションデータ(AppData)と呼ばれています。アプリケーションデータはバージョン0.7ではエスケープした文字列のみサポートしています(次のバージョンではJSONそのものの保存も可能になるようです)。

    var list = $('#friends ul li');
    var intro = "{result:[{";
    for (var i=0; i < list.length; i++) {
      var textarea = list[i].lastChild.lastChild;
      var uid = textarea.id.substring(6);
      var intro_text = textarea.value.replace("'", "\'");;
      intro += "'"+uid+"':'"+intro_text+"'";
      intro += (list.length-1)==i ? "" : ",";
    };
    intro += '}]};';
    var req = opensocial.newDataRequest();
    intro = gadgets.util.escapeString(intro);

この処理は、ユーザーが友達の紹介文を書き終わって「投稿ボタン」を押すことでトリガーされるものです。DOMを辿って各友達のユーザーIDと紹介文の内容を取得する、普通のJavaScriptです。取得した内容はJSONの文字列になるよう連結し、エスケープすることで、アプリケーションデータとして保存が可能になります。

    req.add(req.newUpdatePersonAppDataRequest('VIEWER', 'Introduction', intro));
    req.send(function() {
      $('#message').html('<p>Your introduction has been submitted.');
    });

最後に、JSON形式になった文字列をデータリクエストオブジェクトに追加して送信して、完了です。

まとめ

解説というよりはソースコード並べただけみたいな記事になってしまいましたが、OpenSocialアプリケーションのほとんどがJavaScriptでできてしまうということは、分かったかと思います。次回は外部サーバーとの連携を行うmakeRequestに触れたいと思います。

View Comments add to hatena hatena.comment (2) add to del.icio.us (0) add to livedoor.clip (0) add to Yahoo!Bookmark (0) Total: 2

3月 30 2008

OpenSocialアプリケーションを作る(1)

Published by Eiji under OpenSocial, Widget

OrkutとMySpaceで自作アプリを動かしてみたので、そのレポートです。

まだ仕様が固まっていないのでグレーな部分も多いのですが、OpenSocialはGoogleGadgetと相性が良いらしく、OrkutもMySpaceも、hi5もGoogleGadget前提となっています。というわけで、今回はGoogleGadgetの基本的な作り方とOrkutへのアプリケーション追加方法の解説です。

ガジェットとは何か

GoogleGadgetはiGoogleで動くJavaScriptとHTMLで記述された簡単なアプリケーションです。JavaScriptとHTMLはXML上に埋め込み、設定内容もXMLに記述します。

< ?xml version="1.0" encoding="UTF-8" ?>
<module>
 <moduleprefs title="Blah Blah Gadget"
  description="Gadget Example"
  author_email="***@***.com"
 >
 </moduleprefs>
 <content type="html">
.....
 </content>
</module>

XMLはこんな感じ。Contentの中にJavaScriptとHTMLを記述することで、その内容がiGoogle上やMySpace等のOpenSocialアプリケーションとして表示されます。

ガジェットはJavaScriptを許可していることから、XSSなどの脆弱性を回避するため、iframeを使って別ドメインで動作するようになっています(iGoogleの場合、gmodules.com)。Contentの属性であるtypeを”html”から”url”に変更し、hrefでURL指定すると、iframe内に自分の管理するサーバーを表示することも可能です。

GoogleGadget自体の仕様は掘り下げるとキリがないのでこの辺りで。詳細はリファレンスをご覧ください。

OrkutのSandboxアカウントを取得する

OrkutはGoogle直結ということもあり、OpenSocialの仕様が最も早く反映されるようです。そのOrkutのOpenSocial実験環境はSandboxと呼ばれ、通常のOrkutアカウントを拡張したSandboxアカウントを取得することで、利用可能となります。

アカウントを取得するにはコチラから申請を行ってください。申請が受理されるまでには数日を要するようです。

OrkutにOpenSocialアプリを追加する

無事アカウントの取得ができたら、実際にアプリケーションを試すことができるようになります。ちなみに、どこかのサーバーにGoogleGadgetのXMLファイルを置いておく必要がありますので、Geocitiesでも何でもいいはずですので、ファイルをアップできるところを用意しておきましょう。

Orkut1

Sandboxにログインするとこんな感じ。一見通常のログイン画面と変わりませが、一点だけ:

Orkut2

画面左にアプリケーションを追加するリンクがあります。クリックすると・・・

Orkut3

URLでXMLファイルを指定してアプリケーションを追加することが出来ます。(ちなみにアプリケーションディレクトリはいつもほとんどアプリがありません)

ここでは、僕の作ったアプリケーションで試してみましょう。URLに下記を入力します:

http://devlab.agektmr.com/OpenSocial/FriendIntroducer.xml

「アプリケーションを追加」ボタンを押すと、次の画面に遷移します。

Orkut4

ここでも「アプリケーションを追加」ボタンを押すことで、アプリケーションの追加が完了します。

Orkut5

こんな感じの画面が表示されたら成功。Orkut上に友達がいない方は、僕のアカウントに友達申請してくれてもOKです。

ひとまず、第一回はここまで。

View Comments add to hatena hatena.comment (1) add to del.icio.us (0) add to livedoor.clip (1) add to Yahoo!Bookmark (0) Total: 2

3月 15 2008

OpenSocialの疑問がひとつ解決した

Published by Eiji under OpenSocial, Widget

前提:Google Gadgetでurlタイプを指定した場合、iframe内にはリモートサーバーの内容がそのまま表示されるため、Ajaxで友達情報等を取得しようとすると、ドメイン超えが必要となり、プロキシ経由でサーバー間通信となりRESTful APIがないと役に立たない

どうやらOrkutでは、Content Type=’url’を許可していない模様。

MYSQL database connection using PHP for my gadget ? – Orkut Developer Forum | Google グループ

Content Type=’url’を指定すると404が返るらしい。 404が返ること自体はバグとのことですが、Content Type=’url’が動くようになったところで、ドメインを超えてOpenSocialを利用するにはプロキシを介したRESTful APIによるアクセスが必須であることは確認できました。これがRESTful APIが正式に登場するまでの暫定措置なのかどうかは未確認ですが、前提は誤っていなかったようです。

View Comments add to hatena hatena.comment (0) add to del.icio.us (0) add to livedoor.clip (0) add to Yahoo!Bookmark (0) Total: 0