Class: Koala::Facebook::OAuth

Inherits:
Object
  • Object
show all
Defined in:
lib/koala/oauth.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app_id, app_secret, oauth_callback_url = nil) ⇒ OAuth

Creates a new OAuth client.

Parameters:

  • app_id (String, Integer)

    a Facebook application ID

  • app_secret

    a Facebook application secret

  • oauth_callback_url (defaults to: nil)

    the URL in your app to which users authenticating with OAuth will be sent



15
16
17
18
19
# File 'lib/koala/oauth.rb', line 15

def initialize(app_id, app_secret, oauth_callback_url = nil)
  @app_id = app_id
  @app_secret = app_secret
  @oauth_callback_url = oauth_callback_url
end

Instance Attribute Details

#app_idObject (readonly)

Returns the value of attribute app_id.



8
9
10
# File 'lib/koala/oauth.rb', line 8

def app_id
  @app_id
end

#app_secretObject (readonly)

Returns the value of attribute app_secret.



8
9
10
# File 'lib/koala/oauth.rb', line 8

def app_secret
  @app_secret
end

#oauth_callback_urlObject (readonly)

Returns the value of attribute oauth_callback_url.



8
9
10
# File 'lib/koala/oauth.rb', line 8

def oauth_callback_url
  @oauth_callback_url
end

Instance Method Details

#exchange_access_token(access_token, options = {}) ⇒ Object

Returns A new access token or the existing one, set to expire in 60 days.

Parameters:

  • access_token

    the access token to exchange

  • options (defaults to: {})

    any additional parameters to send to Facebook when exchanging tokens.

Returns:

  • A new access token or the existing one, set to expire in 60 days.

See Also:



246
247
248
249
250
# File 'lib/koala/oauth.rb', line 246

def exchange_access_token(access_token, options = {})
  if info = exchange_access_token_info(access_token, options)
    info["access_token"]
  end
end

#exchange_access_token_info(access_token, options = {}) ⇒ Object

Fetches an access_token with extended expiration time, along with any other information provided by Facebook. See developers.facebook.com/docs/offline-access-deprecation/#extend_token (search for fb_exchange_token).

Parameters:

  • access_token

    the access token to exchange

  • options (defaults to: {})

    any additional parameters to send to Facebook when exchanging tokens.

Returns:

  • the access token with extended expiration time and other information (expiration, etc.)



232
233
234
235
236
237
# File 'lib/koala/oauth.rb', line 232

def exchange_access_token_info(access_token, options = {})
  get_token_from_server({
    :grant_type => 'fb_exchange_token',
    :fb_exchange_token => access_token
  }, true, options)
end

#generate_client_code(access_token) ⇒ Object

Generates a ‘client code’ from a server side long-lived access token. With the generated code, it can be sent to a client application which can then use it to get a long-lived access token from Facebook. After which the clients can use that access token to make requests to Facebook without having to use the server token, yet the server access token remains valid. See developers.facebook.com/docs/facebook-login/access-tokens/#long-via-code

Parameters:

  • access_token

    a user’s long lived (server) access token

Returns:

  • a string of the generated ‘code’

Raises:

  • Koala::Facebook::ServerError if Facebook returns a server error (status >= 500)

  • Koala::Facebook::OAuthTokenRequestError if Facebook returns an error response (status >= 400)

  • Koala::Facebook::BadFacebookResponse if Facebook returns a blank response

  • Koala::KoalaError if response does not contain ‘code’ hash key



150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/koala/oauth.rb', line 150

def generate_client_code(access_token)
  response = fetch_token_string({:redirect_uri => @oauth_callback_url, :access_token => access_token}, false, 'client_code')

  # Facebook returns an empty body in certain error conditions
  if response == ''
    raise BadFacebookResponse.new(200, '', 'generate_client_code received an error: empty response body')
  else
    result = MultiJson.load(response)
  end

  result.has_key?('code') ? result['code'] : raise(Koala::KoalaError.new("Facebook returned a valid response without the expected 'code' in the body (response = #{response})"))
end

#get_access_token(code, options = {}) ⇒ Object

Note:

The server-side authentication and dialog methods should only be used if your application can’t use the Facebook Javascript SDK, which provides a much better user experience. See developers.facebook.com/docs/reference/javascript/.

Fetches the access token (ignoring expiration and other info) from Facebook. Useful when you’ve received an OAuth code using the server-side authentication process.

Parameters:

  • options (defaults to: {})

    any additional parameters to send to Facebook when redeeming the token

  • code

    an OAuth code received from Facebook

Returns:

  • the access token

Raises:

  • Koala::Facebook::OAuthTokenRequestError if Facebook returns an error response

See Also:



195
196
197
198
199
200
# File 'lib/koala/oauth.rb', line 195

def get_access_token(code, options = {})
  # upstream methods will throw errors if needed
  if info = get_access_token_info(code, options)
    string = info["access_token"]
  end
end

#get_access_token_info(code, options = {}) ⇒ Object

Note:

The server-side authentication and dialog methods should only be used if your application can’t use the Facebook Javascript SDK, which provides a much better user experience. See developers.facebook.com/docs/reference/javascript/.

Fetches an access token, token expiration, and other info from Facebook. Useful when you’ve received an OAuth code using the server-side authentication process.

Parameters:

  • options (defaults to: {})

    any additional parameters to send to Facebook when redeeming the token

  • code

    an OAuth code received from Facebook

Returns:

  • a hash of the access token info returned by Facebook (token, expiration, etc.)

Raises:

  • Koala::Facebook::OAuthTokenRequestError if Facebook returns an error response

See Also:



177
178
179
180
181
# File 'lib/koala/oauth.rb', line 177

def get_access_token_info(code, options = {})
  # convenience method to get a parsed token from Facebook for a given code
  # should this require an OAuth callback URL?
  get_token_from_server({:code => code, :redirect_uri => options[:redirect_uri] || @oauth_callback_url}, false, options)
end

#get_app_access_token(options = {}) ⇒ Object

Fetches the application’s access token (ignoring expiration and other info).

Parameters:

  • options (defaults to: {})

    any additional parameters to send to Facebook when redeeming the token

Returns:

  • the application access token

See Also:



219
220
221
222
223
# File 'lib/koala/oauth.rb', line 219

def get_app_access_token(options = {})
  if info = get_app_access_token_info(options)
    string = info["access_token"]
  end
end

#get_app_access_token_info(options = {}) ⇒ Object

Fetches the application’s access token, along with any other information provided by Facebook. See developers.facebook.com/docs/authentication/ (search for App Login).

Parameters:

  • options (defaults to: {})

    any additional parameters to send to Facebook when redeeming the token

Returns:

  • the application access token and other information (expiration, etc.)



208
209
210
211
# File 'lib/koala/oauth.rb', line 208

def get_app_access_token_info(options = {})
  # convenience method to get a the application's sessionless access token
  get_token_from_server({:grant_type => 'client_credentials'}, true, options)
end

#get_token_from_session_key(session, options = {}) ⇒ Object

Deprecated.

Facebook no longer provides session keys.



304
305
306
307
308
# File 'lib/koala/oauth.rb', line 304

def get_token_from_session_key(session, options = {})
  # convenience method for a single key
  # gets the overlaoded strings automatically
  get_tokens_from_session_keys([session], options)[0]
end

#get_token_info_from_session_keys(sessions, options = {}) ⇒ Object

Deprecated.

Facebook no longer provides session keys.



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/koala/oauth.rb', line 278

def get_token_info_from_session_keys(sessions, options = {})
  Koala::Utils.deprecate("Facebook no longer provides session keys. The relevant OAuth methods will be removed in the next release.")

  # fetch the OAuth tokens from Facebook
  response = fetch_token_string({
    :type => 'client_cred',
    :sessions => sessions.join(",")
  }, true, "exchange_sessions", options)

  # Facebook returns an empty body in certain error conditions
  if response == ""
    raise BadFacebookResponse.new(200, '', "get_token_from_session_key received an error (empty response body) for sessions #{sessions.inspect}!")
  end

  MultiJson.load(response)
end

#get_tokens_from_session_keys(sessions, options = {}) ⇒ Object

Deprecated.

Facebook no longer provides session keys.



296
297
298
299
300
301
# File 'lib/koala/oauth.rb', line 296

def get_tokens_from_session_keys(sessions, options = {})
  # get the original hash results
  results = get_token_info_from_session_keys(sessions, options)
  # now recollect them as just the access tokens
  results.collect { |r| r ? r["access_token"] : nil }
end

#get_user_from_cookies(cookies) ⇒ Object Also known as: get_user_from_cookie

Note:

this method can only be called once per session, as the OAuth code Facebook supplies can only be redeemed once. Your application must handle cross-request storage of this information; you can no longer call this method multiple times. (This works out, as the method has to make a call to FB’s servers anyway, which you don’t want on every call.)

Parses the cookie set Facebook’s JavaScript SDK and returns only the user ID.

Parameters:

  • cookie_hash

    a set of cookies that includes the Facebook cookie. You can pass Rack/Rails/Sinatra’s cookie hash directly to this method.

Returns:

  • the authenticated user’s Facebook ID, or nil.



50
51
52
53
54
55
56
57
58
59
60
# File 'lib/koala/oauth.rb', line 50

def get_user_from_cookies(cookies)
  Koala::Utils.deprecate("Due to Facebook changes, you can only redeem an OAuth code once; it is therefore recommended not to use this method, as it will consume the code without providing you the access token. See https://developers.facebook.com/roadmap/completed-changes/#december-2012.")
  if signed_cookie = cookies["fbsr_#{@app_id}"]
    if components = parse_signed_request(signed_cookie)
      components["user_id"]
    end
  elsif info = (cookies)
    # Parsing unsigned cookie
    info["uid"]
  end
end

#get_user_info_from_cookies(cookie_hash) ⇒ Object Also known as: get_user_info_from_cookie

Note:

this method can only be called once per session, as the OAuth code Facebook supplies can only be redeemed once. Your application must handle cross-request storage of this information; you can no longer call this method multiple times. (This works out, as the method has to make a call to FB’s servers anyway, which you don’t want on every call.)

Parses the cookie set Facebook’s JavaScript SDK.

Parameters:

  • cookie_hash

    a set of cookies that includes the Facebook cookie. You can pass Rack/Rails/Sinatra’s cookie hash directly to this method.

Returns:

  • the authenticated user’s information as a hash, or nil.



34
35
36
37
38
39
40
# File 'lib/koala/oauth.rb', line 34

def (cookie_hash)
  if signed_cookie = cookie_hash["fbsr_#{@app_id}"]
    parse_signed_cookie(signed_cookie)
  elsif unsigned_cookie = cookie_hash["fbs_#{@app_id}"]
    parse_unsigned_cookie(unsigned_cookie)
  end
end

#parse_signed_request(input) ⇒ Object

Parses a signed request string provided by Facebook to canvas apps or in a secure cookie.

Parameters:

  • input

    the signed request from Facebook

Returns:

  • a hash of the validated request information

Raises:

  • OAuthSignatureError if the signature is incomplete, invalid, or using an unsupported algorithm



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/koala/oauth.rb', line 259

def parse_signed_request(input)
  encoded_sig, encoded_envelope = input.split('.', 2)
  raise OAuthSignatureError, 'Invalid (incomplete) signature data' unless encoded_sig && encoded_envelope

  signature = base64_url_decode(encoded_sig).unpack("H*").first
  envelope = MultiJson.load(base64_url_decode(encoded_envelope))

  raise OAuthSignatureError, "Unsupported algorithm #{envelope['algorithm']}" if envelope['algorithm'] != 'HMAC-SHA256'

  # now see if the signature is valid (digest, key, data)
  hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @app_secret, encoded_envelope)
  raise OAuthSignatureError, 'Invalid signature' if (signature != hmac)

  envelope
end

#url_for_access_token(code, options = {}) ⇒ Object

Note:

The server-side authentication and dialog methods should only be used if your application can’t use the Facebook Javascript SDK, which provides a much better user experience. See developers.facebook.com/docs/reference/javascript/.

Once you receive an OAuth code, you need to redeem it from Facebook using an appropriate URL. (This is done by your server behind the scenes.) See developers.facebook.com/docs/authentication/.

Parameters:

  • code

    an OAuth code received from Facebook

  • options (defaults to: {})

    any additional query parameters to add to the URL

Returns:

  • an URL your server can query for the user’s access token

Raises:

  • ArgumentError if no OAuth callback was specified in OAuth#new or in options as :redirect_uri

See Also:



110
111
112
113
114
115
116
117
118
# File 'lib/koala/oauth.rb', line 110

def url_for_access_token(code, options = {})
  # Creates the URL for the token corresponding to a given code generated by Facebook
  url_options = {
    :client_id => @app_id,
    :code => code,
    :client_secret => @app_secret
  }.merge(options)
  build_url(:graph_server, "/oauth/access_token", true, url_options)
end

#url_for_dialog(dialog_type, options = {}) ⇒ Object

Note:

The server-side authentication and dialog methods should only be used if your application can’t use the Facebook Javascript SDK, which provides a much better user experience. See developers.facebook.com/docs/reference/javascript/.

Builds a URL for a given dialog (feed, friends, OAuth, pay, send, etc.) See developers.facebook.com/docs/reference/dialogs/.

Parameters:

  • dialog_type

    the kind of Facebook dialog you want to show

  • options (defaults to: {})

    any additional query parameters to add to the URL

Returns:

  • an URL your server can query for the user’s access token



129
130
131
132
133
# File 'lib/koala/oauth.rb', line 129

def url_for_dialog(dialog_type, options = {})
  # some endpoints require app_id, some client_id, supply both doesn't seem to hurt
  url_options = {:app_id => @app_id, :client_id => @app_id}.merge(options)
  build_url(:dialog_host, "/dialog/#{dialog_type}", true, url_options)
end

#url_for_oauth_code(options = {}) ⇒ Object

Note:

The server-side authentication and dialog methods should only be used if your application can’t use the Facebook Javascript SDK, which provides a much better user experience. See developers.facebook.com/docs/reference/javascript/.

Builds an OAuth URL, where users will be prompted to log in and for any desired permissions. When the users log in, you receive a callback with their See developers.facebook.com/docs/authentication/.

Parameters:

  • options (defaults to: {})

    any query values to add to the URL, as well as any special/required values listed below.

Options Hash (options):

Returns:

  • an OAuth URL you can send your users to

Raises:

  • ArgumentError if no OAuth callback was specified in OAuth#new or in options as :redirect_uri

See Also:



85
86
87
88
89
90
91
92
93
94
# File 'lib/koala/oauth.rb', line 85

def url_for_oauth_code(options = {})
  # for permissions, see http://developers.facebook.com/docs/authentication/permissions
  if permissions = options.delete(:permissions)
    options[:scope] = permissions.is_a?(Array) ? permissions.join(",") : permissions
  end
  url_options = {:client_id => @app_id}.merge(options)

  # Creates the URL for oauth authorization for a given callback and optional set of permissions
  build_url(:dialog_host, "/dialog/oauth", true, url_options)
end