Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added optional appsecret_proof configuration parameter. #323

Merged
merged 8 commits into from
Feb 1, 2014
5 changes: 5 additions & 0 deletions lib/koala.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ def configure
def config
@config ||= OpenStruct.new(HTTPService::DEFAULT_SERVERS)
end

# Used for testing.
def reset_config
@config = nil
end
end

# @private
Expand Down
8 changes: 7 additions & 1 deletion lib/koala/api.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
# graph_batch_api and legacy are required at the bottom, since they depend on API being defined
require 'koala/api/graph_api'
require 'koala/api/rest_api'
require 'openssl'

module Koala
module Facebook
class API
# Creates a new API client.
# @param [String] access_token access token
# @param [String] app_secret app secret, for tying your access tokens to your app secret
# @note If no access token is provided, you can only access some public information.
# @return [Koala::Facebook::API] the API client
def initialize(access_token = nil)
def initialize(access_token = nil, app_secret = nil)
@access_token = access_token
@app_secret = app_secret
end

attr_reader :access_token
Expand Down Expand Up @@ -45,6 +48,9 @@ def api(path, args = {}, verb = "get", options = {}, &error_checking_block)
# This is explicitly needed in batch requests so GraphCollection
# results preserve any specific access tokens provided
args["access_token"] ||= @access_token || @app_access_token if @access_token || @app_access_token
if options.delete(:appsecret_proof) && args["access_token"] && @app_secret
args["appsecret_proof"] = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), @app_secret, args["access_token"])
end

# Translate any arrays in the params into comma-separated strings
args = sanitize_request_parameters(args)
Expand Down
1 change: 1 addition & 0 deletions lib/koala/api/graph_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ def batch(http_options = {}, &block)
#
# @return the result from Facebook
def graph_call(path, args = {}, verb = "get", options = {}, &post_processing)
options = options.merge(:appsecret_proof => true)
result = api(path, args, verb, options) do |response|
error = check_response(response.status, response.body)
raise error if error
Expand Down
5 changes: 5 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ friends = @graph.get_connections("me", "friends")
# you can even use the new Timeline API
# see https://developers.facebook.com/docs/beta/opengraph/tutorial/
@graph.put_connections("me", "namespace:action", :object => object_url)

# you can add your appsecret in order to pass the appsecret_proof parameter, tying your access tokens to your app secret:
# see https://developers.facebook.com/docs/reference/api/securing-graph-api/
@graph = Koala::Facebook::API.new(oauth_access_token, app_secret)
# you'll want to turn on 'Require proof on all calls' in the advanced section of your app's settings when doing this.
```

The response of most requests is the JSON data returned from the Facebook servers as a Hash.
Expand Down
106 changes: 106 additions & 0 deletions spec/cases/api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,110 @@
it_should_behave_like "Koala GraphAPI without an access token"
it_should_behave_like "Koala GraphAPI with GraphCollection"
end

context '#api' do
let(:access_token) { 'access_token' }
let(:api) { Koala::Facebook::API.new(access_token) }
let(:path) { '/path' }
let(:appsecret) { 'appsecret' }
let(:token_args) { { 'access_token' => access_token } }
let(:appsecret_proof_args) { { 'appsecret_proof' => OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), appsecret, access_token) } }
let(:verb) { 'get' }
let(:response) { Koala::HTTPService::Response.new(200, '', '') }

describe "with the :appsecret_proof option set" do

describe "and with an access token present" do
describe "and with an appsecret present" do
let(:api) { Koala::Facebook::API.new(access_token, appsecret) }

it "should send the appsecret_proof argument" do
Koala.should_receive(:make_request).with(path, token_args.merge(appsecret_proof_args), verb, {}).and_return(response)

api.api(path, {}, verb, :appsecret_proof => true)
end
end

describe "but without an appsecret present" do
it "should not send the appsecret_proof argument" do
Koala.should_receive(:make_request).with(path, token_args, verb, {}).and_return(response)

api.api(path, {}, verb, :appsecret_proof => true)
end
end
end

describe "but without an access token present" do
describe "and with an appsecret present" do
let(:api) { Koala::Facebook::API.new(nil, appsecret) }

it "should not send the appsecret_proof argument" do
Koala.should_receive(:make_request).with(path, {}, verb, {}).and_return(response)

api.api(path, {}, verb, :appsecret_proof => true)
end
end

describe "but without an appsecret present" do
let(:api) { Koala::Facebook::API.new }

it "should not sent the appsecret_proof argument" do
Koala.should_receive(:make_request).with(path, {}, verb, {}).and_return(response)

api.api(path, {}, verb, :appsecret_proof => true)
end
end
end

end

describe "without the appsecret_proof option set" do

describe "and with an access token present" do
describe "and with an appsecret present" do
let(:api) { Koala::Facebook::API.new(access_token, appsecret) }

it "should not send the appsecret_proof argument" do
Koala.should_receive(:make_request).twice.with(path, token_args, verb, {}).and_return(response)

api.api(path, {}, verb, :appsecret_proof => false)
api.api(path)
end
end

describe "but without an appsecret present" do
it "should not send the appsecret_proof argument" do
Koala.should_receive(:make_request).twice.with(path, token_args, verb, {}).and_return(response)

api.api(path, {}, verb, :appsecret_proof => false)
api.api(path)
end
end
end

describe "but without an access token present" do
describe "and with an appsecret present" do
let(:api) { Koala::Facebook::API.new(nil, appsecret) }

it "should not send the appsecret_proof argument" do
Koala.should_receive(:make_request).twice.with(path, {}, verb, {}).and_return(response)

api.api(path, {}, verb, :appsecret_proof => false)
api.api(path)
end
end

describe "but without an appsecret present" do
let(:api) { Koala::Facebook::API.new }

it "should not send the appsecret_proof argument" do
Koala.should_receive(:make_request).twice.with(path, {}, verb, {}).and_return(response)

api.api(path, {}, verb, :appsecret_proof => false)
api.api(path)
end
end
end
end
end
end
12 changes: 12 additions & 0 deletions spec/cases/graph_api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,16 @@
end
end
end

context '#graph_call' do
describe "the appsecret_proof option" do
let(:path) { '/path' }

it "should be passed to #api" do
@api.should_receive(:api).with(path, {}, 'get', :appsecret_proof => true)

@api.graph_call(path)
end
end
end
end
9 changes: 6 additions & 3 deletions spec/support/graph_api_shared_examples.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
# GRAPH CALL
describe "graph_call" do
it "passes all arguments to the api method" do
args = [KoalaTest.user1, {}, "get", {:a => :b}]
@api.should_receive(:api).with(*args)
@api.graph_call(*args)
user = KoalaTest.user1
args = {}
verb = 'get'
opts = {:a => :b}
@api.should_receive(:api).with(user, args, verb, opts.merge(:appsecret_proof => true))
@api.graph_call(user, args, verb, opts)
end

it "throws an APIError if the result hash has an error key" do
Expand Down
6 changes: 1 addition & 5 deletions spec/support/koala_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,7 @@ def self.setup_rspec

config.after :each do
# Clean up Koala config
Koala.configure do |config|
Koala::HTTPService::DEFAULT_SERVERS.each_pair do |k, v|
config.send("#{k}=", v)
end
end
Koala.reset_config

# if we're working with a real user, clean up any objects posted to Facebook
# no need to do so for test users, since they get deleted at the end
Expand Down