lib/koala.rb in koala-0.8.0 vs lib/koala.rb in koala-0.9.0

- old
+ new

@@ -3,10 +3,13 @@ # rubygems is required to support json, how facebook returns data require 'rubygems' require 'json' +# openssl is required to support signed_request +require 'openssl' + # include default http services require 'koala/http_services' # add Graph API methods require 'koala/graph_api' @@ -47,13 +50,17 @@ end def api(path, args = {}, verb = "get", options = {}, &error_checking_block) # Fetches the given path in the Graph API. args["access_token"] = @access_token || @app_access_token if @access_token || @app_access_token + + # add a leading / + path = "/#{path}" unless path =~ /^\// + # make the request via the provided service result = Koala.make_request(path, args, verb, options) - + # Check for any 500 errors before parsing the body # since we're not guaranteed that the body is valid JSON # in the case of a server error raise APIError.new({"type" => "HTTP #{result.status.to_s}", "message" => "Response body: #{result.body}"}) if result.status >= 500 @@ -187,33 +194,48 @@ def get_app_access_token if info = get_app_access_token_info string = info["access_token"] end end + + # signed_request + def parse_signed_request(request) + # Facebook's signed requests come in two parts -- the signature and the data payload + # see http://developers.facebook.com/docs/authentication/canvas + encoded_sig, payload = request.split(".") + + sig = base64_url_decode(encoded_sig) + # if the signature matches, return the data, decoded and parsed as JSON + if OpenSSL::HMAC.digest("sha256", @app_secret, payload) == sig + JSON.parse(base64_url_decode(payload)) + else + nil + end + end + # from session keys def get_token_info_from_session_keys(sessions) # fetch the OAuth tokens from Facebook response = fetch_token_string({ :type => 'client_cred', :sessions => sessions.join(",") }, true, "exchange_sessions") - # get_token_from_session_key should return an empty body if an empty string or nil is provided - # if invalid tokens are provided, it returns an array of nulls, which is a valid result - if response == "" + # Facebook returns an empty body in certain error conditions + if response == "" raise APIError.new("ArgumentError", "get_token_from_session_key received an error (empty response body) for sessions #{sessions.inspect}!") end JSON.parse(response) end def get_tokens_from_session_keys(sessions) # get the original hash results results = get_token_info_from_session_keys(sessions) # now recollect them as just the access tokens - results.collect { |r| string = r["access_token"] } + results.collect { |r| r ? r["access_token"] : nil } end def get_token_from_session_key(session) # convenience method for a single key # gets the overlaoded strings automatically @@ -240,13 +262,21 @@ end components end def fetch_token_string(args, post = false, endpoint = "access_token") - Koala.make_request("oauth/#{endpoint}", { + Koala.make_request("/oauth/#{endpoint}", { :client_id => @app_id, :client_secret => @app_secret }.merge!(args), post ? "post" : "get").body + end + + # base 64 + def base64_url_decode(string) + # to properly decode what Facebook provides, we need to add == to the end + # and translate certain characters to others before running the actual decoding + # see http://developers.facebook.com/docs/authentication/canvas + "#{string}==".tr("-_", "+/").unpack("m")[0] end end end # finally, set up the http service Koala methods used to make requests