Facebook Graph API With FBML Canvas Apps

Facebook is now sending a new parameter to canvas applications, _signedrequest. This parameter is the concatenation of a JSON object and a signature, both Base64 encoded. The JSON object contains 4 other parameters:

user_id: ID of the logged in user
oauth_token: The users OAuth access token
expires: When the token expires
profile_id: ID of the profile when rendered in tab

The signature is a HMAC-SHA265 of the JSON and is needed to verify that the request originates from Facebook.

With this new parameter it’s finally possible to use the Graph API in an affordably way with FBML apps. Just store the access token in your users session and pass it along with your Graph API requests.

Parsing signed_request

I implemented support for the _signedrequest to my Facebook library for Rails. The code I’m using for verifying the signature and adding the JSON params to the Rails params via Rack middleware looks like this (extract):

class ParamsParser
  def initialize(app, &condition)
    @app = app
    @condition = condition

  def call(env)
    request = Rack::Request.new(env)

    signed_request = request.params["signed_request"]
    signature, signed_params = signed_request.split('.')

    # Verify signature
    unless signed_request_is_valid?(FB['secret'], signature, signed_params)
      return Rack::Response.new(["Invalid Facebook signature"], 400).finish

    # Parse JSON
    signed_params = Yajl::Parser.new.parse(base64_url_decode(signed_params))

    # Add JSON parameters to Rails params
    signed_params.each do |k,v|
      request.params[k] = v


  def signed_request_is_valid?(secret, signature, params)
    signature = base64_url_decode(signature)
    expected_signature = OpenSSL::HMAC.digest(
      'SHA256', secret, params.tr("-_", "+/"))
    return signature == expected_signature

  # Stolen from mini_fb.
  # Ruby's implementation of base64 decoding reads the string in multiples of
  # 6 and ignores any extra bytes.
  # Since facebook does not take this into account, this function fills any
  # string with white spaces up to
  # the point where it becomes divisible by 6, then it replaces '-' with '+'
  # and '_' with '/' (URL-safe decoding),
  # and decodes the result.
  def base64_url_decode(str)
    str = str + "=" * (6 - str.size % 6) unless str.size % 6 == 0
    return Base64.decode64(str.tr("-_", "+/"))

Using the Graph API

Using the Graph API is just as simple as this:

curl https://graph.facebook.com/me?access_token=YOUR_TOKEN

This will respond with a JSON object containing your basic user info.

Talking to the Graph API with Ruby / HTTParty is not much more complicated then using curl:

HTTParty.get("https://graph.facebook.com/me", :access_token => "YOUR TOKEN")

Old params deprecation

In the future Facebook will deprecate all other parameters than _signedrequest, but there’s no exact timing right now.

There’s some official documentation on how to migrate from the old params to _signedrequest available at developers.facebook.com.

UPDATE 2010-07-26

Facebook silently stopped sending the _signedrequest parameter.

You can enable them again by activating the “OAuth 2.0 for Canvas (beta)” migration in you app settings on Facebook, but this disables all other parameters. So make sure to migrate your app to only rely on signed_request.

Bitly API

I created a small and simple library to access the bitly API.

Usage is simple:

>> api = BitlyApi::Bitly.new(:login => BITLY_LOGIN, :api_key => BITLY_API_KEY)
=> #<BitlyApi::Bitly:0x7f93177c87e0 @api_version="2.0.1", @api_key=BITLY_API_KEY, @login=BITLY_LOGIN>
>> api.shorten "http://pickhost.eu"
=> {"userHash"=>"2wzb4d", "hash"=>"zNgN6", "shortKeywordUrl"=>"", "shortUrl"=>"http://bit.ly/2wzb4d"}
>> api.expand "2wzb4d"
=> {"longUrl"=>"http://pickhost.eu/"}
>> api.stats "2wzb4d"
=> {"userClicks"=>1, "referrers"=>{""=>{"direct"=>1}}, "userHash"=>"2wzb4d", "hash"=>"zNgN6", "clicks"=>1, "userReferrers"=>{""=>{"direct"=>1}}}
>> api.info "2wzb4d"
=> {"thumbnail"=>{"small"=>"http://s.bit.ly/bitly/zNgN6/thumbnail_small.png", "medium"=>"http://s.bit.ly/bitly/zNgN6/thumbnail_medium.png", "large"=>"http://s.bit.ly/bitly/zNgN6/thumbnail_large.png"}, "id3"=>{}, "userHash"=>"2wzb4d", "hash"=>"zNgN6", "mirrorUrl"=>"", "htmlMetaDescription"=>"An image hosting service with simplicity and speed in mind.", "version"=>1.0, "htmlTitle"=>"Pic(k)host :: Upload your Images!", "globalHash"=>"zNgN6", "calaisId"=>"", "calais"=>{}, "users"=>["pickhost"], "surbl"=>0, "longUrl"=>"http://pickhost.eu/", "htmlMetaKeywords"=>["Bilder hochladen", "Bilder upload", "Foto upload", "Bilderhoster", "Imagehoster", "Foto", "Bild", "Photo", "Image", "Pic", "Hoster"], "contentType"=>"text/html; charset=utf-8", "keyword"=>"", "exif"=>{}, "contentLength"=>"", "shortenedByUser"=>"pickhost", "metacarta"=>[], "keywords"=>[], "calaisResolutions"=>{}}

The project is located at GitHub.