Facebook Night vol.7 発表内容まとめ 1:publish_streamとpublish_actions」で主要な2パーミッションの違いを、「Facebook Night vol.7 発表内容まとめ 2:認証ダイアログの構成」では、それら2つの認証ダイアログ上での扱いの違いを紹介しました。その過程で、ユーザがアプリをインストールしたからと言って、全部のパーミッションを許可しているとは限らないことや、あとからFacebook上の設定ページでパーミッションが消されうることを話しました。ここでは、それに対応する実装方法を紹介します。

与えられたパーミッションを確認する

例えば機能の1つとして、ユーザの好きな本一覧を取得し、そこからおススメの本をレコメンドするような機能を提供していたとします。user_likesパーミッションが許可されなかったり消されたりしたら必要なデータを読み込めなくなってしまいますから、算出方法を切り替えるとか、場合によっては該当ユーザへのレコメンド機能自体を非表示にする必要が出てくるかもしれません。
それには、ユーザがどのパーミッションを許可してくれているのか、少なくともユーザがサービスを利用する旅に知る必要があります。これにはGraph APIのユーザオブジェクトのpermissionsコネクションを参照します。
以下の2種類のいずれかを用います。
curl -X GET "https://graph.facebook.com/me/permissions?access_token=USER_ACCESS_TOKEN"
curl -X GET "https://graph.facebook.com/{USER_ID}/permissions?access_token=APP_ACCESS_TOKEN"
返される値は以下のようになります。
{
  "data": [
    {
      "installed": 1,
      "publish_actions": 1,
      "user_likes": 1,
    }
  ]
}
許可されたパーミッションをキー、許可されているか否かの真偽値を値に持ちますが、installedというのだけ若干例外で、アプリをインストール済みか否かを指します。ユーザのアプリインストール時やログイン時、このAPIを利用してどのパーミッションが与えられているのかをチェックすることになります。

Batch Request

ログイン時の認証の流れは、ザックリと以下のようになります。
  • 認証ダイアログのページへリダイレクト
  • 完了後、アプリ側(redirect_uri)へリダイレクト
  • 渡されたcodeパラメータを元にアクセストークン取得
  • ユーザ情報取得
これらの処理の後にパーミッション一覧を取得する手順を挟むと、Grpah APIへのリクエストが一回余計に発生してしまいます。そういった場合に複数のGraph APIリクエストをまとめてしまうのがBatch Requestと呼ばれる物なのですが、Facebook Nightで発表した内容は全て「ログイン時、Batch Requestでユーザのパーミッション一覧を取得する」で紹介していますので、そちらをご覧ください。

Real-time Update API

場合によっては、ユーザのログイン時だけでなく、よりリアルタイムにパーミッションの変化を知りたい場合があります。例えばユーザのステータス投稿を定期的に取得したいならuser_statusパーミッションが必要となりますが、このパーミッションがFacebook上の設定画面から削除されてしまった場合、Batch Requestで紹介した方法ではユーザが次にログインするまで削除されたことを知ることができず、定期実行時にエラーが出続けることになります。
Real-time Update APIを利用すると、Facebook上でGraph APIオブジェクトやそのコネクションに変化があった際にリアルタイムに受け取ることが可能です。PubSubHubbubに詳しい方は取っ付きやすいかもしれませんが、渡されるのがJSONだったりするなどの違いがあります。quoraで紹介されている限りでは、昔はPubSubHubbubで実装されていたものの、他のGraph APIの仕様との一貫性などの理由から変更したそうです。

流れ

どの情報を購読するのか、どのコールバックURLで通知を受けるのかを、Graph APIを用いて設定する必要があります。流れは以下の通りです。
rt
Subscriberであるアプリが、Publisher / HubとなるFacebookへリクエストを送り、どのOpen Graphオブジェクトのどのフィールドの変化を購読したいのか伝えます。このリクエストに対するレスポンスが返されるより先に、FacebookはアプリのコールバックURLに対して確認用のリクエストを送ってきます。ここで適切なレスポンスを返すと確認手順が完了し、購読が成立して最初のリクエストに対するレスポンスが返されます。

それでは詳細を見てみます。

Step1. リクエスト送信

アプリがFacebook側へリクエストを送り、購読の対象を指定します。

curl -X POST http://graph.facebook.com/{APP_ID}subscriptions \

 -F "object=permissions" \                                                                                                                                                       

 -F "fields=email,publish_actions,user_birthday,read_friendlists" \

 -F "callback_url={CALLBACK_URL}" \

 -F "verify_token={APP_GENERATED_TOKEN}" \

 -H "Authorization: OAuth {APP_ACCESS_TOKEN}"

この際のパラメータは以下の通りです。
  • object: 購読する対象のオブジェクトのタイプを指定します。指定できるのはuser、page、permissionsのいずれかです。今回はpermissionsを指定します。
  • fields: 上記objectのプロパティもしくはコネクションの中で、購読したいもの。カンマ区切りでパーミッションを指定します。
  • callback_url: objectとfieldsで指定した対象に変化があったときに通知を受け取るURL。
  • verify_token: アプリ側で指定するトークンです。Facebookからの確認用リクエストで送られてきます。

Step2. 確認用リクエスト受信

FacebookからアプリのコールバックURLへとリクエストが送られてきます。
curl -X GET "{CALLBACK_URL}?hub.mode=subscribe&hub.challenge=XXXX&hub.verify_token={APP_GENERATED_TOKEN}"
ここで渡されるパラメータは以下の通りです。
  • hub.mode: これは常にsubscribeが渡ります。
  • hub.verify_token: 最初のリクエストでこちらから渡したverify_tokenがそのまま渡ってきます。アプリ側での確認用です。
  • hub.challenge: Facebookが生成して渡してくるランダムな文字列。レスポンスとして、status200と共に返します。

Step3. レスポンスを返す

Facebookから渡されたhub.verify_tokenがアプリ側から送信したverify_tokenと一致することを確かめたら、HTTP Status 200とverify_tokenの値を返します。

Step4. 最初のリクエストのレスポンスを受け取る

Step3までが正しく完了すればstatus 200が返り、不備があればstatus 400が返ります。

購読状況を確認する

購読中の内容を確認するには以下のエンドポイントにアクセスします。
curl -X GET "https://graph.facebook.com/{APP_ID}/subscriptions?access_token={APP_ACCESS_TOKEN}"
返り値は以下のキャプチャの通りです。userオブジェクトのevents, locale, nameフィールドを購読していることが分かります。話が逸れてしまいますが、この発表の前の週にReal-time Update APIのドキュメントに変更があり、userオブジェクトのeventsコネクションも購読できるようになりました。userオブジェクトのドキュメントには反映されていませんが、テストした限りでは、キャプチャの通り購読できているようです。

sub

購読の解除

任意のオブジェクトのみ購読解除
curl -X DELETE "https://graph.facebook.com/{APP_ID}/subscriptions?object=XXX"
全購読解除
curl -X DELETE "https://graph.facebook.com/{APP_ID}/subscriptions"
objectパラメータを付け忘れると全購読が解除されますので注意してください。
また、ここに限らず、Graph APIでHTTP DELETEメソッドを使う箇所はmethod=deleteパラメータ指定でのHTTP POSTリクエストでも代用できます。

通知

実際の通知内容は以下のキャプチャのようになります。
20faed988943194f1dc877e756c2a221
ここでは変更内容が伝えられることは無く、どのオブジェクトのどのフィールド / コネクションが変更されたかのみ通知されます。キャプチャのように配列で渡ってくるのは、変更の都度、完全なリアルタイムで送られてくるわけではなく、5秒に一回、もしくは未通知の変更が1000件を超えたときに一気に送られてくる為です。通知に失敗した場合は、徐々に頻度を下げつつ、24時間の間はリトライされます。
こうして通知されたオブジェクトIDとフィールドを元に、Graph APIで最新情報を取得してくることになります。変更されたオブジェクトの情報がある程度まとめて送られてきますので、ここでもBatch Requestを活用すると効果的です。

ユーザがFacebook上でアプリを削除してしまったら?

アプリで退会処理を踏まず、Facebook上の設定・管理画面からアプリを削除されてしまうこともあり得ます。そういった場合の対処方法は「ユーザがFacebookアプリを削除したときに通知を受けるには」を参照してください。

おまけ

ドキュメントの更新が激しくて、それに追いつくの大変ですよねという話をしました。公式ブログのエントリーで、その週に更新されたドキュメントURLがリストアップされていたこともありましたが、該当ページのどの部分が更新されているかを把握するのも面倒でした。また、前任者が実装したシェアボタンのドキュメントが、シェアボタンといいね!ボタンの統合で消えてしまったりして、あとから追いようが無いという問題もありました。
そこで、6月上旬からはFBDocsで https://developers.facebook.com/docs/* のページをバージョン管理しています。左ナビゲーションのリンク先も、ドキュメント内のリンクであれば href="/docs/XXX" に置き換えてありますので、git cloneしてきて確認したいバージョンに戻しておいて、ブラウザで http://localhost/docs/XXX にアクセスして確認すると、当時のバージョンのまま遷移もできて少し便利かなと思っています。
今後ドキュメントの和訳を更新する際は、githubのコミットログのページか何かへのリンクを張るとかして、どの段階まで対応しているかを明示します。「この和訳の更新が追いついていないんだけど」とかあったらOklahomerページにメッセージ送るなどしてお知らせください。