1月 08 2009

FriendConnect向けあしあとガジェット公開とコード解説

FriendIntroducerはFriendConnectを試すことに主眼を置いて作ったガジェットだったため、それほど実用的なものではありませんでした。そこで、本格的に使えるガジェットとして、MyBlogLogやgooあしあと、Yahooログール的ガジェットとしてFootprintsを作りました。

Footprintsとは?

Footprintsは、サイトメンバーの訪問を記録するガジェットです。百聞は一見にしかず、このブログの左ペイン一番下にあるガジェットをご覧ください。もしこのサイトのFriendConnectに登録していなければ、Joinしてみてください。ログインしていない場合はしてください。 Footprints1 ご想像通り、ログインした状態でブログを訪問すると、あしあととして訪問記録が残ります。ほかの人が見ると、どのくらい前に訪問したかが分かります。また、最近流行の(?)あしあとを消す機能も実装済みです。 ガジェットXMLはhttp://devlab.agektmr.com/OpenSocial/FriendConnect/Footprints.xmlになりますので、欲しい方はご自由にお持ちください。貼り方はこちらを参考にしてください。

コードと動作の解説

FriendConnectとはいえ、中身はただのOpenSocialガジェットです。OpenSocial本も発売間近、mixigooホームのOpenSocial対応も近いということで、今回はサンプルコード付きでガジェットの内容を解説してみたいと思います。

基本的な動作

まず、あしあとの記録方式ですが、FriendConnectは残念ながらmakeRequest未対応のため、外部サーバーのDBに保存、なんてことはできません。AppData(Persistent API)を使って保存しています。AppDataはガジェット+ユーザーの組み合わせごとに存在するのですが、残念ながらOWNERであるブログ自体のAppDataには保存できないことが判明しました。この辺りはPermissionが絡んでくるのですが、一般的なOpenSocialとは事情が異なるため、特殊な権限の割り振りになっていると思われます。 この問題を解決するために考えたのが、VIEWERごとに自分のAppData領域にデータを保存する、という技。OWNERの友達のAppDataをまとめて取得すれば、似たような状況が作れるはずです。今回はこの方式でうまく実現することができました。 つまり、1つのデータ領域に配列であしあとを記録して行くのではなく、メンバーごとに最終アクセス日時をあしあととして記録し、Persistent APIのnewFetchPeopleAppDataでメンバー全員分をまとめて取得しています。

コード解説

        init:function() {
          // データリクエストオブジェクトを生成
          var req = opensocial.newDataRequest();
          // 閲覧者(viewer)の情報を取得
          var params = {};
          params[opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS] = [opensocial.Person.Field.PROFILE_URL];
          req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER, params), 'viewer');
          // メンバー(OWNERの友達)のAppDataをまとめて取得
          var idspec = opensocial.newIdSpec({'userId':opensocial.IdSpec.PersonId.OWNER,
                                             'groupId':opensocial.IdSpec.GroupId.FRIENDS});
          req.add(req.newFetchPersonAppDataRequest(idspec, 'footprint'), 'footprint');

          // 上記2つの取得リクエストをまとめて投げ、callback関数で受け取ります
          req.send(function(response) {
            if (!response.get('viewer').hadError()) {
              fp.viewer = response.get('viewer').getData();
            }
            if (response.get('footprint').hadError()) {
              fp.footprints = [];
            } else {
              var footprints = response.get('footprint').getData();
              var exist = false;
              // AppDataはユーザーごとに返ってきますので、ループで回します
              $.each(footprints, function(footprint) {
                // AppDataは文字列しか受け付けませんので、取得時にJSONオブジェクトに戻してやります
                var json = gadgets.util.unescapeString(this.footprint);
                var foot = gadgets.json.parse(json);
                if (fp.viewer !== null) {
                  // 閲覧者と一致した場合は上書き
                  if (foot.id == fp.viewer.getId()) {
                    exist = true;
                    foot.id = fp.viewer.getId();
                    foot.name = fp.viewer.getDisplayName(),
                    foot.thumbnail = fp.viewer.getField(opensocial.Person.Field.THUMBNAIL_URL),
                    foot.profile = fp.viewer.getField(opensocial.Person.Field.PROFILE_URL),
                    foot.timestamp = (new Date()).getTime();
                    // 新しいあしあとをAppDataに記録
                    fp.setFootprint(foot);
                  }
                }
                fp.footprints.unshift(foot);
              });
              if (!exist && fp.viewer !== null) {
                // 今まで一度もあしあとを記録したことがない人の場合、追加します
                var foot = {'id':         fp.viewer.getId(),
                            'name':       fp.viewer.getDisplayName(),
                            'thumbnail':  fp.viewer.getField(opensocial.Person.Field.THUMBNAIL_URL),
                            'profile':    fp.viewer.getField(opensocial.Person.Field.PROFILE_URL),
                            'timestamp':  (new Date()).getTime()};
                fp.footprints.unshift(foot);
                fp.setFootprint(foot);
              }
              // 時系列でソート
              fp.footprints.sort(function(a, b) {
                return b.timestamp - a.timestamp;
              });
              // レンダリングします
              fp.showFootprints();
            }
          });
        },

ユーザーごとにAppDataを保存する部分は下記のコードです。

        setFootprint:function(foot) {
          // あしあとのデータオブジェクトを文字列に変換
          var str = gadgets.json.stringify(foot);
          var req = opensocial.newDataRequest();
          // 閲覧者のAppDataに記録
          req.add(req.newUpdatePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, 'footprint', str));
          req.send();
        },

OpenSocial的に役立ちそうな部分のみ抜粋していますが、上記コードで基本的な動作を行っています。ソースコード全体を読みたい方はこちらから。

まとめ

FrienConnectの特殊な環境としては、前述のPermission問題。それからmakeRequestの問題が分かってきました。いずれもプライバシーの重要性に配慮した結果と思われます。 Permissionについては、一般的なSNS上のOpenSocialでは、VIEWER自身がガジェットをインストールしているかどうかで挙動が変わりますが、FriendConnectではOWNERが常に仮想人格であることから、そもそもその前提がなりたたないため、特殊な動きをしているようです。 makeRequestについては、技術的な問題は既に解決済みのはずですので、特殊なPermission下で取得されたプライバシーに関わるデータを外部サーバーに安易に保存されることを避ける狙いがあるのではないでしょうか? 何はともあれ、このガジェットを使うと、「ああ、ブログをコミュニティに変えるってこういうことなのか」と、(少しだけ)感じることができます。ぜひお試しください。

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

10月 29 2008

米YahooがOpenSocialに対応

Published by Eiji under SocialWeb

本日米Yahoo!から、Yahoo! Open Strategy 1.0として、ディベロッパ向けにYahoo! Application Platform (YAP)Yahoo! Social Platform (YSP), Yahoo! Query Language (YQL)リリースされました。

Yahoo! Social Platform

プロフィールやアドレス帳、更新情報等、ソーシャルにまつわるAPIをRESTベースで提供するものです。認証機構にはOAuthを利用し、PHP版、Flash版のライブラリも提供されていますが、このREST APIはOpenSocial互換ではありません。

Yahoo! Query Language

SQLライクなコマンドを送信する事でYahoo! Pipesのようにデータ取得が可能なウェブベースのAPI。FacebookのFQLのようなもののようです。

Yahoo! Application Platform

Yahoo!上で動作する埋め込み型のアプリケーション。OpenSocialのガジェットプラットフォームは現時点ではサポートされていませんが、JavaScript APIは使えるようです。大きく2つのビューがあります。

Small View

HTMLまたはYML Liteのみサポート。JavaScriptはサポートされていません。YMLは、Facebookで言うところのFBMLのようなもので、 My!Yahoo等様々なページにパーツとして表示する事が想定されています。

Canvas View

ディベロッパが指定したURLが出力したYMLをプロキシして表示するタイプのアプリケーション。Facebookライクな仕組みですね。もちろん、サーバーサイドでYahoo! Social Platformを使ったプログラムを書く事で、OAuthで認証を行い、RESTベースでソーシャルグラフを取得したり、コンタクトリストを取得したりすることできます。

また、OpenSocial JavaScript API(v0.8)にも対応しているので、クライアントサイドから更新情報を追加するといった利用も可能な様子。Cajaが利用されているので、セキュリティに気遣う事なく実装できそうです。(いつのまに実用レベルに達していたのでしょうか・・・)

時間のある時にでもサンプルアプリケーションを作ってみたいと思います。

所感

今回のYahoo!のリリースは、FacebookプラットフォームとOpenSocialのいいとこ取りといった感じ。ただし、OpenSocialに完全に準拠している訳ではないので、他で作ったアプリケーションをちょっとだけ書き換えて転用、という訳には行かなそうです。

例えば、クライアントからmakeRequestを使って外部サーバーのデータを取得するようなJavaScript APIは利用する事ができません。(追記:Gadgets Core APIは利用できるようです。ただし、Pref、Viewなど、featureで指定する機能は利用できません。)また、OpenSocialならHTMLを出力すればよかったものが、YMLを出力しなければなりません。等々・・

ただ、世界最大のポータルサイトがOpenSocialに対応することの意義は大きく、今後の動きは要注目です。全体の戦略からすれば、OpenSocialの対応はあくまでパーツに過ぎず、今後のウェブのあり方を位置づける重要な意味を持ちます。先日行われたYahoo! Open Hack Dayのプレゼンテーションは必見です。

どんどんプラットフォーム化していくウェブと、その中で占めるソーシャル機能の持つ重要性が、日本でも認識される日はそう遠くないと思います。

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

8月 06 2008

Data AvailabilityでOAuthを試す

Published by Eiji under OAuth

前エントリでの予告通り、実際にサーバーサイドでコードを書き、MySpaceのData Availabilityを使ってOAuthを試してみます。Data Availabilityという名前は大げさに聞こえるかもしれませんが、実際はOpenSocial RESTful APIです。ちなみにData AvailabilityではまだJSON形式のみのサポートで、AtomPubには対応していません(しかも404が返ってくる。これに相当ハマった○| ̄|_)。

今回はOAuthを使って認証・認可を取得し、Data Availability APIを叩くところまでを解説します。

下準備

まずはサンドボックス環境にMySpaceにアプリを作ってください。細かい手順が分からない方はこの辺を参考にしてください。

MySpaceではガジェットアプリも外部アプリも同じように扱われるようです。

Edit Detailsを開くと、アプリケーションの詳細設定を編集することができます。

ここでOAuthの利用に必要なものを思い出してください。まずはコンシューマキー(consumer_key)とコンシューマシークレット(consumer_secret)です。

MySpaceの場合、アプリケーションを登録した段階でこれら2つが発行されます。コンシューマキーについては好きなものに変更できますが、ここではアプリケーションのガジェットXMLぽいURLにしてみました。後で必要になりますので、どこかにコピペっておきましょう。

次に、同じページの下の方にExternal Site Settingsという項目があります。これがData Availabilityの肝です。

  • Use External Domainにチェックを入れる
  • External URLにMySpaceからの誘導先URLを入力
  • External Domainに実際に外部アプリを置くサーバーのドメインを入力
  • 利用規約を読んで同意

これで準備オッケー。

OAuthを実装する

今回試すのは上図の外部サービス、つまりコンシューマに当たる部分です。サービスプロバイダに当たるのはMySpace。ゼロから実装してもよいのですが、せっかく便利なライブラリがありますので、これのPHP版を使って試してみます。また、署名方式はHMAC-SHA1を使います。

OAuthのフローは下記の通り。この辺りを読んで仕様を理解しておく事をお勧めします。

  1. リクエストトークンを取得
  2. ユーザー認証
  3. アクセストークンを取得
  4. リソースにアクセス

リクエストトークンを取得

必要なライブラリをインクルードします。

require_once 'oauth/OAuth.php';
require_once 'oauth/OAuth_TestServer.php';

各種変数をセットしておきましょう。先程メモったconsumer_keyconsumer_secretはここで使います。リクエストトークンを取得するためのエンドポイントはMySpaceのドキュメントに記載されています。

$consumer['key'] = 'http://devlab.agektmr.com/MyOpenSpace/DataAvailabilityExample';
$consumer['secret'] = '************';
$endpoint = 'http://api.myspace.com/request_token';

署名のロジックはめんどくさいのでライブラリにお任せ。

$server = new TestOAuthServer(new MockOAuthDataStore());
$server->add_signature_method(new OAuthSignatureMethod_HMAC_SHA1());

$sig_methods = $server->get_signature_methods();
$sig_method = $sig_methods['HMAC-SHA1'];

$consumer = new OAuthConsumer($consumer['key'], $consumer['secret'], NULL);
$request = OAuthRequest::from_consumer_and_token($consumer, NULL, "GET", $endpoint, null);
$request->sign_request($sig_method, $consumer, NULL);

$req = curl_init($request);
curl_setopt($req, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($req);

ここまでのコードで$resultにリクエストトークンが返ってくることになります。URLのquery部と同じ形式で返ってきますので、必要に応じてパースしましょう。

parse_str($result, $tmp);

これで、oauth_tokenoauth_token_secretが取得できたはずです。

認証

次にユーザーに認証を行ってもらいます。エンドポイントはhttp://api.myspace.com/authorizeで行います。その際、先程取得したoauth_tokenoauth_callbackをパラメータとして付属します。oauth_callbackは認証後に呼び出されるページのURL。

$callback_url = 'http://devlab.agektmr.com/MyOpenSpace/access.php';
$auth_url = 'http://api.myspace.com/authorize?oauth_token='.urlencode($tokens['oauth_token']).
    '&oauth_callback='.urlencode($callback_url);

アクセストークンを取得

先程指定したoauth_callbackのURLにoauth_tokenをパラメータとして付属してリダイレクトされてきます。これはこのoauth_tokenが認証済みであることを示しており、アクセストークンへの交換が可能となります。

$consumer = new OAuthConsumer($consumer['key'], $consumer['secret'], NULL);
$tokener  = new OAuthConsumer($tokens['oauth_token'], $tokens['oauth_token_secret']);
$access = OAuthRequest::from_consumer_and_token($consumer, $tokener, "GET", $endpoint, null);
$access->sign_request($sig_method, $consumer, $tokener);

$req = curl_init($access);
curl_setopt($req, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($req);

コードはリクエストトークン取得の際とあまり変わりありません。これでアクセストークンのoauth_tokenoauth_token_secretが返っきたら準備オッケー。

RESTful APIを叩く

ここまでに取得したconsumer_keyconsumer_secretoauth_tokenoauth_token_secretを使って署名したOAuthリクエストをRESTful APIに投げることにより、友達リストなどのデータ取得が可能になります。

$consumer = new OAuthConsumer($consumer['key'], $consumer['secret'], NULL);
$tokener  = new OAuthConsumer($tokens['oauth_token'], $tokens['oauth_token_secret']);
$resource = OAuthRequest::from_consumer_and_token($consumer, $tokener, "GET", $endpoint, array('format'=>'JSON'));
$resource->sign_request($sig_method, $consumer, $tokener);

$req = curl_init($resource);
curl_setopt($req, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($req);

これでエンドポイント($endpoint)を取得したいリソースのURLにすればOK。レスポンスボディにJSON形式でデータが返ってきます。

サンプルアプリ

上記コードを使って一連の流れを見ながら動作を確認できるサンプルを用意しました。どういうリクエストを投げるのか参考になると思います。

実際に動作するサンプルはコチラ

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

8月 02 2008

OpenSocialのOAuthまとめ

Published by Eiji under OAuth, OpenSocial

OpenSocialでは、コンテナが外部サーバーとの通信を行う際、または外部サーバーがコンテナと通信を行う際、OAuthを使用して認可を行います。今回はOpenSocialにおけるOAuthについて、現段階でのまとめを書いてみます。 ※追記(2008/10/20):2008/10/4に書いたコチラの記事も必読です。

OAuthって何だったっけ?

OAuthはユーザーコンシューマサービスプロバイダの3者間でデータのやり取りを行うとした場合、ユーザーがコンシューマにクレデンシャル(IDやパスワード)を渡すことなく、ユーザーが所有するサービスプロバイダ上のリソースにコンシューマをアクセスさせるためのものです。 例えばユーザーGoogle(サービスプロバイダ)アドレス帳(リソース)MySpace(コンシューマ)上で利用するシーンを思い浮かべてください。OAuthがなければ、MySpaceにGoogleのIDとパスワードを預けなければならなかったものが、OAuthを使うことで、ユーザーが直接Googleと認証のやりとりを行い、MySpaceにGoogleのID/パスワードを渡すことなく、アドレス帳のデータをMySpaceに渡すことができるようになります。

2種類のOAuth

さて、そんな便利なOAuthですが、OpenSocialで利用されるものには2種類あります。

OAuth Core

OAuth Coreでは、先程説明したように、ユーザーコンシューマサービスプロバイダの3者間でやり取りを行います。ベーシックなものですので、詳細についてはこの辺りを参考にしてください。

OAuth Consumer Request

一方OAuth Consumer Requestは、OAuthの仕様からユーザー認証部分を除き、コンシューマとサービスプロバイダのやり取りにフォーカスした仕様で、一般に”two-legged OAuth“と呼ばれます。これはコンシューマとサービスプロバイダの信頼関係だけで、ユーザーによる認証を伴わない仕様のため、コンシューマがサービスプロバイダからパブリックな情報を取得したい場合に利用するケースが想定されます。 (かなり恥ずかしい間違いです。正確にはコンシューマが署名を付加することで、サービスプロバイダがリクエスト元とリクエスト内容に間違いがないことを確認できる仕様、です。/ 2009年10月追記)ちなみにOpenSocial v0.7ではOAuth Coreの利用は仕様に含まれておらず、このtwo-legged OAuthを利用することになっています。OAuth Coreが利用できるのはOpenSocial v0.8以降での話になります(もちろん、two-legged OAuthも利用できます)。

OpenSocialにおけるOAuth利用パターン

OpenSocialでOAuthを利用する形態として、さらに2通りが考えられます。

ガジェットが外部サーバーとやり取りを行うOutbound OAuth

ここでは仮にOutbound OAuthと呼びます。type=”html”で作られたガジェットが、SNSコンテナをプロキシとしてコンシューマの役割を果たし、サービスプロバイダとなる外部サーバーとmakeRequestで通信を行うケースです。

外部サーバーがコンテナとやり取りを行うInbound OAuth

ここでは仮にInbound OAuthと呼びます。コンシューマとなる外部サーバーがサービスプロバイダであるSNSコンテナのRESTful APIを叩くケースです。type=”url”のガジェットが外部サーバーを通してSNSコンテナのRESTful APIを叩くケースもこれに該当します。

OAuthの利用に必要なもの

OAuthの利用には前提条件がいくつか存在します。細かい仕様は別途調べていただくとして、事前に必要な条件が下記になります。

  • コンシューマが、サービスプロバイダの発行する以下を事前に知っていること。
    • コンシューマキー(consumer_key)
    • コンシューマシークレット(consumer_secret)
  • コンシューマが、サービスプロバイダとOAuthのやり取りを行う以下3つのURLを知っていること
    • サービスプロバイダのリクエストトークンURL
    • サービスプロバイダのアクセストークンURL
    • サービスプロバイダの認証URL

※追記(2008/10/20):コンシューマシークレットについては、署名方式がRSA-SHA1の場合、必須ではありません。詳しくはコチラ。 OAuth利用パターンごとにどのようにしてこの条件をクリアするかを検証してみます。

Outbound OAuthのケース

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

<oauth>
<service name="google">
<request url="https://www.google.com/accounts/OAuthGetRequestToken?scope=http://www.google.com/m8/feeds/" />
<access url="https://www.google.com/accounts/OAuthGetAccessToken" />
<authorization url="https://www.google.com/accounts/OAuthAuthorizeToken" />
</service>
</oauth>

OAuthは必ずしも1つのサーバーとやり取りするとは限りませんので、Serviceを追加することで複数をサポートすることができるようになっています。Service@nameで使い分けることが出来るようになっていますので、必要に応じてmakeRequestのopt_paramsに下記のパラメータを加え、サービスを指定してください。

gadgets.io.RequestParameters.OAUTH_SERVICE_NAME

サービスプロバイダとOAuthのやり取りを行うURLについては、XRDS-Simpleによって解決する方法もありますが、こちらについては別の機会にまとめてご紹介します。

Inbound OAuthのケース

外部アプリケーションがSNSコンテナのRESTful APIにアクセスする場合になります。これはまさに、FacebookのFacebook ConnectやMySpaceのData Availability、GoogleのFriendConnectに該当するもので、まだ実験的な段階にあると言えるものです。 コンシューマキーとコンシューマシークレットですが、SNSコンテナ上でアプリケーションを登録することで発行されます。それをディベロッパがメモ/コピペしてコンシューマとなるサーバーのコードに埋め込みましょう。URLについては、単純にヘルプページを見る方法と、XRDS-Simpleによるオートディスカバリを行う方法が考えられます。

まとめ

今回は大まかな話を書きましたが、次回は実際にMySpaceのData Availabilityを使ってOAuth認証を行い、データを取得するところまでを試してみたいと思います。

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

7月 10 2008

OpenSocial API仕様(v0.8)を翻訳しました

Published by Eiji under OpenSocial

OpenSocial API仕様(v0.8)

一ヶ月くらい前から出来てたのですが、一応ブログ記事にも書いておきます。何か間違い等ありましたらお知らせください。

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

Next »