Facebook Signed Request を Ruby on Rails で扱う
最近いろいろな事情があり、Facebookアプリなるものを作ったりしているのですが、「Facebookページタブ埋め込み型のアプリ」を制作するに当たり、避けては通れない「Facebook Signed Request」の扱い方をまとめてみました。
Signed Requestとは?
Facebook Signed Request は、Webアプリを「Facebookアプリ」として動作させた際にアプリへのパラメータとして渡される各種情報のことです。
Signed Requestを利用すると、アプリ側で以下のような情報を得ることが出来ます。
- タブとして埋め込まれている「元のFacebookページ」のIDを取得出来る。
- アプリを表示しているユーザが、「元のFacebookページ」の管理者かどうかを取得出来る。
- アプリを表示しているユーザが、「元のFacebookページ」の「いいね!」を押しているかどうかを判定出来る。
参考にしたURL:
ログインフローを手作業で構築する - Facebookログイン - ドキュメンテーション - 開発者向けFacebook
json - Decoding Facebook's signed request in Ruby/Sinatra - Stack Overflow
Base64デコード
Signed RequestはBase64でエンコードされている為、デコードする為のメソッドを用意します。
def base64_url_decode(str) encoded_str = str.gsub('-','+').gsub('_','/') encoded_str += '=' while !(encoded_str.size % 4).zero? Base64.decode64(encoded_str) end
Signed Request本体をBase64デコードして、JSONをHashに変換
Signed Request全体のうち、ドットで区切られた前半がsignature、後半がリクエスト本体です。
今回はRails環境なので、ActiveSupport::JSONを使ってサクッとHashに変換します。
def decode_data(str) encoded_sig, payload = str.split('.') data = ActiveSupport::JSON.decode(base64_url_decode(payload)) end
Signed Requestの妥当性チェック
リクエスト本体から生成したダイジェストが、signatureと等しいことをチェックします。ダイジェスト生成時のキーには、Facebookアプリの設定画面に表示されている「App secret」を使用します。
ダイジェストの生成にはruby-hmac gem を使っています。
(OpenSSL::HMACが使える環境であればそちらのほうがベターかと思いますが、OpenSSLのバージョンが古い環境だと、SHA256でのダイジェスト生成が出来ない可能性もあるので注意。)
APP_SECRET = "[Facebookアプリのsecret]" def verify_signature(str) encoded_sig, payload = str.split('.') sig = base64_url_decode(encoded_sig) expected_sig = HMAC::SHA256.digest(APP_SECRET, payload) sig == expected_sig end
Signed Requestの取得
アプリ側から見ると「params[:signed_request]」として取得出来る為、妥当性チェックの後にJSONとして取得します。
def signed_request if params[:signed_request] && verify_signature(params[:signed_request]) decode_data(params[:signed_request]) else nil end end
Signed Requestの使い方
取得したSigned Requestは、以下のように使います。
# 埋め込まれているFacebookページのIDは? signed_request["page"]["id"] # => '1111111111111' # いいね!が押されているか? signed_request["page"]["liked"] # => true # ページ管理者か? signed_request["page"]["admin"] # => true
注意事項
- params[:signed_request]がアプリに渡されるのは、アプリ設定の「キャンバスページURL」で指定したURLに対してのみです。
そのため、キャンバスページが表示される最初のタイミングで、必要に応じてセッションに格納するなどの処置が必要です。 - ところが、上記の流れでセッションを使うと、IE環境でセッションが保持されないという問題が発生するはず。
これは、IEのデフォルトではiframe内で別ドメインのサイトを表示した場合、Cookieが有効にならないために発生する現象とのことです。
そんなときは、以下のURLを参考に対処します。(もちろん意味を理解した上でね!)
P3Pコンパクトポリシーをコピペするのが流行らないことを祈る | 水無月ばけらのえび日記
class ApplicationController < ActionController::Base ... def ie_p3p_fix if request.env["HTTP_USER_AGENT"] =~ /MSIE/ response.headers["P3P"] = 'CP="CAO PSA OUR"' end end before_filter :ie_p3p_fix ...