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