koala 0.10.0 → 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,19 @@
1
+ v1.0
2
+ New methods:
3
+ -- Photo and file upload now supported through #put_picture and general support for file hashes (see documentation)
4
+ -- Added a delete_like method (thanks to wassem)
5
+ Internal improvements:
6
+ -- For public requests, Koala now uses http by default (instead of https) to improve speed
7
+ -- This can be overridden through Koala.always_use_ssl= or by passing :use_ssl => true in the options hash for an api call
8
+ -- Read-only REST API requests now go through the faster api-read server
9
+ -- Replaced parse_signed_request with a version from Facebook that also supports the new signed params proposal
10
+ -- Note: invalid requests will now raise exceptions rather than return nil, in keeping with other SDKs
11
+ -- delete_object will now raise an error if there's no access token (like put_object and delete_like)
12
+ Test improvements:
13
+ -- Expanded HTTP service tests (added Typhoeus test suite and additional Net::HTTP test cases)
14
+ -- Live tests now verify that the access token has the necessary permissions before starting
15
+ -- Replaced the 50-person network test, which often took 15+ minutes to run live, with a 5-person test
16
+
1
17
  v0.10.0
2
18
  -- Added test user module
3
19
  -- Fixed bug when raising APIError after Facebook fails to exchange session keys
@@ -11,10 +27,10 @@ v0.9.1
11
27
  v0.9.0
12
28
  -- Added parse_signed_request to handle Facebook's new authentication scheme
13
29
  -- note: creates dependency on OpenSSL (OpenSSL::HMAC) for decryption
14
- -- Added GraphCollection class to provide paging support for GraphAPI get_connections and search methods (thanks to jagthedrummer)
30
+ -- Added GraphCollection class to provide paging support for GraphAPI get_connections and search methods (thanks to jagthedrummer)
15
31
  -- Added get_page method to easily fetch pages of results from GraphCollections
16
32
  -- Exchanging sessions for tokens now works properly when provided invalid/expired session keys
17
- -- You can now include a :typhoeus_options key in TyphoeusService#make_request's options hash to control the Typhoeus call (for example, to set :disable_ssl_peer_verification => true)
33
+ -- You can now include a :typhoeus_options key in TyphoeusService#make_request's options hash to control the Typhoeus call (for example, to set :disable_ssl_peer_verification => true)
18
34
  -- All paths provided to HTTP services start with leading / to improve compatibility with stubbing libraries
19
35
  -- If Facebook returns nil for search or get_connections requests, Koala now returns nil rather than throwing an exception
20
36
 
@@ -23,7 +39,7 @@ v0.8.0
23
39
  -- Removed string overloading for the methods, per 0.7.3, which caused Marshaling issues
24
40
  -- Removed ability to provide a string as the second argument to url_for_access_token, per 0.5.0
25
41
 
26
- v0.7.4
42
+ v0.7.4
27
43
  -- Fixed bug with get_user_from_cookies
28
44
 
29
45
  v0.7.3
@@ -34,18 +50,18 @@ v0.7.3
34
50
  -- Using those methods triggers a deprecation warning
35
51
  -- This will be removed by 1.0
36
52
  -- There are new info methods (get_access_token_info, get_app_access_token_info, get_token_info_from_session_keys, and get_user_info_from_cookies) that natively return hashes, for when you want the expiration date
37
- -- Responses with HTTP status 500+ now properly throw errors under Net::HTTP
53
+ -- Responses with HTTP status 500+ now properly throw errors under Net::HTTP
38
54
  -- Updated changelog
39
55
  -- Added license
40
56
 
41
57
  v0.7.2
42
58
  -- Added support for exchanging session keys for OAuth access tokens (get_token_from_session_key for single keys, get_tokens_from_session_keys for multiple)
43
59
  -- Moved Koala files into a koala/ subdirectory to minimize risk of name collisions
44
- -- Added OAuth Playground git submodule as an example
60
+ -- Added OAuth Playground git submodule as an example
45
61
  -- Updated tests, readme, and changelog
46
62
 
47
63
  v0.7.1
48
- -- Updated RealtimeUpdates#list_subscriptions and GraphAPI#get_connections to now return an
64
+ -- Updated RealtimeUpdates#list_subscriptions and GraphAPI#get_connections to now return an
49
65
  array of results directly (rather than a hash with one key)
50
66
  -- Fixed a bug with Net::HTTP-based HTTP service in which the headers hash was improperly formatted
51
67
  -- Updated readme
@@ -92,7 +108,7 @@ v0.3.1
92
108
  v0.3
93
109
  -- Renamed Graph API class from Facebook::GraphAPI to FacebookGraph::API
94
110
  -- Created FacebookGraph::OAuth class for tokens and OAuth URLs
95
- -- Updated method for including HTTP service (think we've got it this time)
111
+ -- Updated method for including HTTP service (think we've got it this time)
96
112
  -- Updated tests
97
113
  -- Added CHANGELOG and gemspec
98
114
 
data/Manifest CHANGED
@@ -13,9 +13,11 @@ lib/koala/test_users.rb
13
13
  readme.md
14
14
  spec/facebook_data.yml
15
15
  spec/koala/api_base_tests.rb
16
+ spec/koala/assets/beach.jpg
16
17
  spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb
17
18
  spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb
18
19
  spec/koala/graph_api/graph_api_no_access_token_tests.rb
20
+ spec/koala/graph_api/graph_api_tests.rb
19
21
  spec/koala/graph_api/graph_api_with_access_token_tests.rb
20
22
  spec/koala/graph_api/graph_collection_tests.rb
21
23
  spec/koala/live_testing_data_helper.rb
@@ -23,8 +25,10 @@ spec/koala/net_http_service_tests.rb
23
25
  spec/koala/oauth/oauth_tests.rb
24
26
  spec/koala/realtime_updates/realtime_updates_tests.rb
25
27
  spec/koala/rest_api/rest_api_no_access_token_tests.rb
28
+ spec/koala/rest_api/rest_api_tests.rb
26
29
  spec/koala/rest_api/rest_api_with_access_token_tests.rb
27
30
  spec/koala/test_users/test_users_tests.rb
31
+ spec/koala/typhoeus_service_tests.rb
28
32
  spec/koala_spec.rb
29
33
  spec/koala_spec_helper.rb
30
34
  spec/koala_spec_without_mocks.rb
data/Rakefile CHANGED
@@ -4,14 +4,14 @@ require 'rake'
4
4
  require 'echoe'
5
5
 
6
6
  # gem management
7
- Echoe.new('koala', '0.10.0') do |p|
8
- p.summary = "A lightweight, flexible library for Facebook with support for the Graph API, the old REST API, realtime updates, and OAuth validation."
9
- p.description = "Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write access to the social graph via the Graph API and the older REST API, as well as support for realtime updates and OAuth and Facebook Connect authentication. Koala is fully tested and supports Net::HTTP and Typhoeus connections out of the box and can accept custom modules for other services."
7
+ Echoe.new('koala', '1.0.0.beta') do |p|
8
+ p.summary = "A lightweight, flexible library for Facebook with support for the Graph API, the old REST API, realtime updates, and OAuth validation."
9
+ p.description = "Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write access to the social graph via the Graph API and the older REST API, as well as support for realtime updates and OAuth and Facebook Connect authentication. Koala is fully tested and supports Net::HTTP and Typhoeus connections out of the box and can accept custom modules for other services."
10
10
  p.url = "http://github.com/arsduo/koala"
11
11
  p.author = ["Alex Koppel", "Chris Baclig", "Rafi Jacoby", "Context Optional"]
12
12
  p.email = "alex@alexkoppel.com"
13
13
  p.ignore_pattern = ["tmp/*", "script/*", "pkg/*"]
14
- p.runtime_dependencies = ["json >=1.0"]
14
+ p.runtime_dependencies = ["json >=1.0", "multipart-post >=1.0"]
15
15
  p.development_dependencies = []
16
16
  p.retain_gemspec = true
17
17
  end
@@ -2,32 +2,34 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{koala}
5
- s.version = "0.10.0"
5
+ s.version = "1.0.0.beta"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Alex Koppel, Chris Baclig, Rafi Jacoby, Context Optional"]
9
- s.date = %q{2010-12-15}
9
+ s.date = %q{2011-01-26}
10
10
  s.description = %q{Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write access to the social graph via the Graph API and the older REST API, as well as support for realtime updates and OAuth and Facebook Connect authentication. Koala is fully tested and supports Net::HTTP and Typhoeus connections out of the box and can accept custom modules for other services.}
11
11
  s.email = %q{alex@alexkoppel.com}
12
12
  s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "lib/koala.rb", "lib/koala/graph_api.rb", "lib/koala/http_services.rb", "lib/koala/realtime_updates.rb", "lib/koala/rest_api.rb", "lib/koala/test_users.rb"]
13
- s.files = ["CHANGELOG", "LICENSE", "Manifest", "Rakefile", "init.rb", "koala.gemspec", "lib/koala.rb", "lib/koala/graph_api.rb", "lib/koala/http_services.rb", "lib/koala/realtime_updates.rb", "lib/koala/rest_api.rb", "lib/koala/test_users.rb", "readme.md", "spec/facebook_data.yml", "spec/koala/api_base_tests.rb", "spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb", "spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb", "spec/koala/graph_api/graph_api_no_access_token_tests.rb", "spec/koala/graph_api/graph_api_with_access_token_tests.rb", "spec/koala/graph_api/graph_collection_tests.rb", "spec/koala/live_testing_data_helper.rb", "spec/koala/net_http_service_tests.rb", "spec/koala/oauth/oauth_tests.rb", "spec/koala/realtime_updates/realtime_updates_tests.rb", "spec/koala/rest_api/rest_api_no_access_token_tests.rb", "spec/koala/rest_api/rest_api_with_access_token_tests.rb", "spec/koala/test_users/test_users_tests.rb", "spec/koala_spec.rb", "spec/koala_spec_helper.rb", "spec/koala_spec_without_mocks.rb", "spec/mock_facebook_responses.yml", "spec/mock_http_service.rb"]
13
+ s.files = ["CHANGELOG", "LICENSE", "Manifest", "Rakefile", "init.rb", "koala.gemspec", "lib/koala.rb", "lib/koala/graph_api.rb", "lib/koala/http_services.rb", "lib/koala/realtime_updates.rb", "lib/koala/rest_api.rb", "lib/koala/test_users.rb", "readme.md", "spec/facebook_data.yml", "spec/koala/api_base_tests.rb", "spec/koala/assets/beach.jpg", "spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb", "spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb", "spec/koala/graph_api/graph_api_no_access_token_tests.rb", "spec/koala/graph_api/graph_api_tests.rb", "spec/koala/graph_api/graph_api_with_access_token_tests.rb", "spec/koala/graph_api/graph_collection_tests.rb", "spec/koala/live_testing_data_helper.rb", "spec/koala/net_http_service_tests.rb", "spec/koala/oauth/oauth_tests.rb", "spec/koala/realtime_updates/realtime_updates_tests.rb", "spec/koala/rest_api/rest_api_no_access_token_tests.rb", "spec/koala/rest_api/rest_api_tests.rb", "spec/koala/rest_api/rest_api_with_access_token_tests.rb", "spec/koala/test_users/test_users_tests.rb", "spec/koala/typhoeus_service_tests.rb", "spec/koala_spec.rb", "spec/koala_spec_helper.rb", "spec/koala_spec_without_mocks.rb", "spec/mock_facebook_responses.yml", "spec/mock_http_service.rb"]
14
14
  s.homepage = %q{http://github.com/arsduo/koala}
15
- s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Koala", "--main", "readme.md"]
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Koala"]
16
16
  s.require_paths = ["lib"]
17
17
  s.rubyforge_project = %q{koala}
18
- s.rubygems_version = %q{1.3.6}
18
+ s.rubygems_version = %q{1.4.2}
19
19
  s.summary = %q{A lightweight, flexible library for Facebook with support for the Graph API, the old REST API, realtime updates, and OAuth validation.}
20
20
 
21
21
  if s.respond_to? :specification_version then
22
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
23
22
  s.specification_version = 3
24
23
 
25
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
24
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
26
25
  s.add_runtime_dependency(%q<json>, [">= 1.0"])
26
+ s.add_runtime_dependency(%q<multipart-post>, [">= 1.0"])
27
27
  else
28
28
  s.add_dependency(%q<json>, [">= 1.0"])
29
+ s.add_dependency(%q<multipart-post>, [">= 1.0"])
29
30
  end
30
31
  else
31
32
  s.add_dependency(%q<json>, [">= 1.0"])
33
+ s.add_dependency(%q<multipart-post>, [">= 1.0"])
32
34
  end
33
35
  end
@@ -5,8 +5,9 @@ require 'digest/md5'
5
5
  require 'rubygems'
6
6
  require 'json'
7
7
 
8
- # openssl is required to support signed_request
8
+ # OpenSSL and Base64 are required to support signed_request
9
9
  require 'openssl'
10
+ require 'base64'
10
11
 
11
12
  # include default http services
12
13
  require 'koala/http_services'
@@ -24,7 +25,7 @@ require 'koala/realtime_updates'
24
25
  require 'koala/test_users'
25
26
 
26
27
  module Koala
27
-
28
+
28
29
  module Facebook
29
30
  # Ruby client library for the Facebook Platform.
30
31
  # Copyright 2010 Facebook
@@ -40,7 +41,7 @@ module Koala
40
41
  # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
41
42
  # License for the specific language governing permissions and limitations
42
43
  # under the License.
43
- #
44
+ #
44
45
  # This client library is designed to support the Graph API and the official
45
46
  # Facebook JavaScript SDK, which is the canonical way to implement
46
47
  # Facebook authentication. Read more about the Graph API at
@@ -53,7 +54,7 @@ module Koala
53
54
  @access_token = access_token
54
55
  end
55
56
  attr_reader :access_token
56
-
57
+
57
58
  def api(path, args = {}, verb = "get", options = {}, &error_checking_block)
58
59
  # Fetches the given path in the Graph API.
59
60
  args["access_token"] = @access_token || @app_access_token if @access_token || @app_access_token
@@ -63,20 +64,20 @@ module Koala
63
64
 
64
65
  # make the request via the provided service
65
66
  result = Koala.make_request(path, args, verb, options)
66
-
67
+
67
68
  # Check for any 500 errors before parsing the body
68
69
  # since we're not guaranteed that the body is valid JSON
69
70
  # in the case of a server error
70
71
  raise APIError.new({"type" => "HTTP #{result.status.to_s}", "message" => "Response body: #{result.body}"}) if result.status >= 500
71
-
72
- # Parse the body as JSON and check for errors if provided a mechanism to do so
72
+
73
+ # Parse the body as JSON and check for errors if provided a mechanism to do so
73
74
  # Note: Facebook sometimes sends results like "true" and "false", which aren't strictly objects
74
75
  # and cause JSON.parse to fail -- so we account for that by wrapping the result in []
75
76
  body = response = JSON.parse("[#{result.body.to_s}]")[0]
76
77
  if error_checking_block
77
78
  yield(body)
78
79
  end
79
-
80
+
80
81
  # now return the desired information
81
82
  if options[:http_component]
82
83
  result.send(options[:http_component])
@@ -85,57 +86,57 @@ module Koala
85
86
  end
86
87
  end
87
88
  end
88
-
89
+
89
90
  class GraphAPI < API
90
91
  include GraphAPIMethods
91
92
  end
92
-
93
+
93
94
  class RestAPI < API
94
95
  include RestAPIMethods
95
96
  end
96
-
97
+
97
98
  class GraphAndRestAPI < API
98
99
  include GraphAPIMethods
99
100
  include RestAPIMethods
100
101
  end
101
-
102
+
102
103
  class RealtimeUpdates < API
103
104
  include RealtimeUpdateMethods
104
105
  end
105
-
106
+
106
107
  class TestUsers < API
107
108
  include TestUserMethods
108
109
  # make the Graph API accessible in case someone wants to make other calls to interact with their users
109
110
  attr_reader :graph_api
110
111
  end
111
-
112
+
112
113
  class APIError < Exception
113
114
  attr_accessor :fb_error_type
114
115
  def initialize(details = {})
115
- self.fb_error_type = details["type"]
116
+ self.fb_error_type = details["type"]
116
117
  super("#{fb_error_type}: #{details["message"]}")
117
118
  end
118
119
  end
119
-
120
-
120
+
121
+
121
122
  class OAuth
122
123
  attr_reader :app_id, :app_secret, :oauth_callback_url
123
124
  def initialize(app_id, app_secret, oauth_callback_url = nil)
124
125
  @app_id = app_id
125
126
  @app_secret = app_secret
126
- @oauth_callback_url = oauth_callback_url
127
+ @oauth_callback_url = oauth_callback_url
127
128
  end
128
129
 
129
130
  def get_user_info_from_cookie(cookie_hash)
130
131
  # Parses the cookie set by the official Facebook JavaScript SDK.
131
- #
132
+ #
132
133
  # cookies should be a Hash, like the one Rails provides
133
- #
134
+ #
134
135
  # If the user is logged in via Facebook, we return a dictionary with the
135
136
  # keys "uid" and "access_token". The former is the user's Facebook ID,
136
137
  # and the latter can be used to make authenticated requests to the Graph API.
137
138
  # If the user is not logged in, we return None.
138
- #
139
+ #
139
140
  # Download the official Facebook JavaScript SDK at
140
141
  # http://github.com/facebook/connect-js/. Read more about Facebook
141
142
  # authentication at http://developers.facebook.com/docs/authentication/.
@@ -150,21 +151,21 @@ module Koala
150
151
 
151
152
  # generate the signature and make sure it matches what we expect
152
153
  auth_string = components.keys.sort.collect {|a| a == "sig" ? nil : "#{a}=#{components[a]}"}.reject {|a| a.nil?}.join("")
153
- sig = Digest::MD5.hexdigest(auth_string + @app_secret)
154
+ sig = Digest::MD5.hexdigest(auth_string + @app_secret)
154
155
  sig == components["sig"] && (components["expires"] == "0" || Time.now.to_i < components["expires"].to_i) ? components : nil
155
156
  end
156
157
  end
157
158
  alias_method :get_user_info_from_cookies, :get_user_info_from_cookie
158
-
159
+
159
160
  def get_user_from_cookie(cookies)
160
161
  if info = get_user_info_from_cookies(cookies)
161
162
  string = info["uid"]
162
163
  end
163
164
  end
164
165
  alias_method :get_user_from_cookies, :get_user_from_cookie
165
-
166
+
166
167
  # URLs
167
-
168
+
168
169
  def url_for_oauth_code(options = {})
169
170
  # for permissions, see http://developers.facebook.com/docs/authentication/permissions
170
171
  permissions = options[:permissions]
@@ -174,54 +175,71 @@ module Koala
174
175
  raise ArgumentError, "url_for_oauth_code must get a callback either from the OAuth object or in the options!" unless callback
175
176
 
176
177
  # Creates the URL for oauth authorization for a given callback and optional set of permissions
177
- "https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}"
178
+ "https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}"
178
179
  end
179
-
180
+
180
181
  def url_for_access_token(code, options = {})
181
182
  # Creates the URL for the token corresponding to a given code generated by Facebook
182
183
  callback = options[:callback] || @oauth_callback_url
183
184
  raise ArgumentError, "url_for_access_token must get a callback either from the OAuth object or in the parameters!" unless callback
184
185
  "https://#{GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
185
186
  end
186
-
187
+
187
188
  def get_access_token_info(code)
188
189
  # convenience method to get a parsed token from Facebook for a given code
189
190
  # should this require an OAuth callback URL?
190
191
  get_token_from_server(:code => code, :redirect_uri => @oauth_callback_url)
191
192
  end
192
-
193
+
193
194
  def get_access_token(code)
194
195
  # upstream methods will throw errors if needed
195
- if info = get_access_token_info(code)
196
- string = info["access_token"]
196
+ if info = get_access_token_info(code)
197
+ string = info["access_token"]
197
198
  end
198
199
  end
199
-
200
+
200
201
  def get_app_access_token_info
201
- # convenience method to get a the application's sessionless access token
202
+ # convenience method to get a the application's sessionless access token
202
203
  get_token_from_server({:type => 'client_cred'}, true)
203
204
  end
204
-
205
+
205
206
  def get_app_access_token
206
207
  if info = get_app_access_token_info
207
- string = info["access_token"]
208
+ string = info["access_token"]
208
209
  end
209
210
  end
210
-
211
- # signed_request
212
- def parse_signed_request(request)
213
- # Facebook's signed requests come in two parts -- the signature and the data payload
214
- # see http://developers.facebook.com/docs/authentication/canvas
215
- encoded_sig, payload = request.split(".")
216
-
217
- sig = base64_url_decode(encoded_sig)
218
-
219
- # if the signature matches, return the data, decoded and parsed as JSON
220
- if OpenSSL::HMAC.digest("sha256", @app_secret, payload) == sig
221
- JSON.parse(base64_url_decode(payload))
222
- else
223
- nil
224
- end
211
+
212
+ # provided directly by Facebook
213
+ # see https://github.com/facebook/crypto-request-examples/blob/master/sample.rb
214
+ # and http://developers.facebook.com/docs/authentication/canvas/encryption_proposal
215
+ def parse_signed_request(input, max_age = 3600)
216
+ encoded_sig, encoded_envelope = input.split('.', 2)
217
+ envelope = JSON.parse(base64_url_decode(encoded_envelope))
218
+ algorithm = envelope['algorithm']
219
+
220
+ raise 'Invalid request. (Unsupported algorithm.)' \
221
+ if algorithm != 'AES-256-CBC HMAC-SHA256' && algorithm != 'HMAC-SHA256'
222
+
223
+ raise 'Invalid request. (Too old.)' \
224
+ if algorithm == "AES-256-CBC HMAC-SHA256" && envelope['issued_at'].to_i < Time.now.to_i - max_age
225
+
226
+ raise 'Invalid request. (Invalid signature.)' \
227
+ if base64_url_decode(encoded_sig) !=
228
+ OpenSSL::HMAC.hexdigest(
229
+ 'sha256', @app_secret, encoded_envelope).split.pack('H*')
230
+
231
+ # for requests that are signed, but not encrypted, we're done
232
+ return envelope if algorithm == 'HMAC-SHA256'
233
+
234
+ # otherwise, decrypt the payload
235
+ cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
236
+ cipher.decrypt
237
+ cipher.key = @app_secret
238
+ cipher.iv = base64_url_decode(envelope['iv'])
239
+ cipher.padding = 0
240
+ decrypted_data = cipher.update(base64_url_decode(envelope['payload']))
241
+ decrypted_data << cipher.final
242
+ return JSON.parse(decrypted_data.strip)
225
243
  end
226
244
 
227
245
  # from session keys
@@ -231,69 +249,68 @@ module Koala
231
249
  :type => 'client_cred',
232
250
  :sessions => sessions.join(",")
233
251
  }, true, "exchange_sessions")
234
-
252
+
235
253
  # Facebook returns an empty body in certain error conditions
236
- if response == ""
254
+ if response == ""
237
255
  raise APIError.new({
238
- "type" => "ArgumentError",
256
+ "type" => "ArgumentError",
239
257
  "message" => "get_token_from_session_key received an error (empty response body) for sessions #{sessions.inspect}!"
240
258
  })
241
259
  end
242
-
260
+
243
261
  JSON.parse(response)
244
262
  end
245
-
263
+
246
264
  def get_tokens_from_session_keys(sessions)
247
265
  # get the original hash results
248
266
  results = get_token_info_from_session_keys(sessions)
249
267
  # now recollect them as just the access tokens
250
268
  results.collect { |r| r ? r["access_token"] : nil }
251
269
  end
252
-
270
+
253
271
  def get_token_from_session_key(session)
254
272
  # convenience method for a single key
255
273
  # gets the overlaoded strings automatically
256
274
  get_tokens_from_session_keys([session])[0]
257
275
  end
258
-
276
+
259
277
  protected
260
-
278
+
261
279
  def get_token_from_server(args, post = false)
262
280
  # fetch the result from Facebook's servers
263
281
  result = fetch_token_string(args, post)
264
-
282
+
265
283
  # if we have an error, parse the error JSON and raise an error
266
284
  raise APIError.new((JSON.parse(result)["error"] rescue nil) || {}) if result =~ /error/
267
285
 
268
286
  # otherwise, parse the access token
269
- parse_access_token(result)
287
+ parse_access_token(result)
270
288
  end
271
-
289
+
272
290
  def parse_access_token(response_text)
273
291
  components = response_text.split("&").inject({}) do |hash, bit|
274
292
  key, value = bit.split("=")
275
293
  hash.merge!(key => value)
276
294
  end
277
- components
295
+ components
278
296
  end
279
297
 
280
298
  def fetch_token_string(args, post = false, endpoint = "access_token")
281
299
  Koala.make_request("/oauth/#{endpoint}", {
282
- :client_id => @app_id,
300
+ :client_id => @app_id,
283
301
  :client_secret => @app_secret
284
- }.merge!(args), post ? "post" : "get").body
302
+ }.merge!(args), post ? "post" : "get", :use_ssl => true).body
285
303
  end
286
-
304
+
287
305
  # base 64
288
- def base64_url_decode(string)
289
- # to properly decode what Facebook provides, we need to add == to the end
290
- # and translate certain characters to others before running the actual decoding
291
- # see http://developers.facebook.com/docs/authentication/canvas
292
- "#{string}==".tr("-_", "+/").unpack("m")[0]
306
+ # directly from https://github.com/facebook/crypto-request-examples/raw/master/sample.rb
307
+ def base64_url_decode(str)
308
+ str += '=' * (4 - str.length.modulo(4))
309
+ Base64.decode64(str.gsub('-', '+').gsub('_', '/'))
293
310
  end
294
311
  end
295
312
  end
296
-
313
+
297
314
  # finally, set up the http service Koala methods used to make requests
298
315
  # you can use your own (for HTTParty, etc.) by calling Koala.http_service = YourModule
299
316
  def self.http_service=(service)
@@ -302,7 +319,6 @@ module Koala
302
319
 
303
320
  # by default, try requiring Typhoeus -- if that works, use it
304
321
  begin
305
- require 'typhoeus'
306
322
  Koala.http_service = TyphoeusService
307
323
  rescue LoadError
308
324
  Koala.http_service = NetHTTPService