-
Notifications
You must be signed in to change notification settings - Fork 464
OAuth
Omniauth and the Omniauth Facebook plugin are fantastic tools for logging users in and getting an token to access the Facebook APIs on their behalf. (This is what I do in my own projects.)
There may be cases in which you want to handle OAuth processing yourself, and that's cool. Koala includes an OAuth class, which helps with several common authentication-related tasks: verifying cookies, verifying signatures from Facebook, and generates authentication URLs.
Facebook applications can also get their own access tokens, which can be used to manage realtime updates and perform certain other sessionless activities. To get your app's access token, just run:
@oauth = Koala::Facebook::OAuth.new(app_id, app_secret)
@oauth.get_app_access_token
# => #{app_access_token}
# these don't expire, but you can still get the hash with get_app_access_token_info
The OAuth references below are likely somewhat out of date relative to Facebook's libraries -- Omniauth handles many of the cases that these methods used to do. If you see an error, I'd be super grateful if you open an issue to let me know.
In order to use OAuth -- the clean and modern way to verify yourself to Facebook -- you have to get tokens for your users. If you're using the Javascript library, you can get the OAuth token from the cookies it creates (see below for non-Javascript methods). Facebook's Javascript library (formerly Facebook Connect) is awesome -- small, clean, and very useful for adding social touches to your website. It'll take care of logging the user in and it handles all the OAuth token craziness behind the scenes. All you have to do is verify the cookies are legit -- the user is indeed who his browser says he is.
Let's test that. I've taken some real-live cookies from a website a friend and I are working on, which uses Koala for cookie validation. (I've manually added some line breaks for legibility.)
{"fbs_190889632882"=>"\"access_token=190889632882|2.U25mFtixF8Pqth45AtnsBQ__.3600.1272909600-2905623|
DNjswq9QfKDrP60TY76Tv8GxCc.&expires=1272909600&secret=QR_id58vqV_qW7MnfJlmLw__&
session_key=2.U25mFtixF8Pqth45AtnsBQ__.3600.1272909600-2905623&
sig=a76960c0c3669470f7ca53b53e034ac4&uid=2905623\"", [other cookies]...}
The important cookie is the fbs_#{app_id} cookie, which contains a string (complete with inner quotes) containing all the information you need to verify Facebook has, indeed, confirmed this user.
To verify the cookies, just instantiate an instance of Koala's OAuth helper:
@oauth = Koala::Facebook::OAuth.new(app_id, app_secret)
# => #<Koala::Facebook::OAuth:0x1017177b0 @app_id=#{your_app_id}, @app_secret=#{your_secret_code}>
And then run:
@oauth.get_user_info_from_cookies(cookies)
# => {"session_key"=>"2.U25mFtixF8Pqth45AtnsBQ__.3600.1272909600-2905623",
# "expires"=>"1272909600", "uid"=>"2905623", "sig"=>"a76960c0c3669470f7ca53b53e034ac4",
# "secret"=>"QR_id58vqV_qW7MnfJlmLw__", "access_token"=>"190889632882|2.U25mFtixF8Pqth45AtnsBQ__.
# 3600.1272909600-2905623|DNjswq9Q-fKDrP60TY76Tv8GxCc."
There you go -- you have a validated user ID and an access token to boot. (I've manually added some line breaks for legibility.) Of course, if the cookies aren't valid (expired, falsified, etc.) the method will simply return nil.
For those of us building tab and canvas apps, Facebook has implemented a new authentication scheme ("signed request") for canvas and tab apps. Fortunately, Koala makes it easy to decode and validate the parameters Facebook provides (ensure to enable [signed_request for Canvas] in the advanced app configurations):
# http://developers.facebook.com/docs/authentication/signed_request/
# in Rails, this would usually be available as params[:signed_request]
signed_request = "vlXgu64BQGFSQrY0ZcJBZASMvYvTHu9GQ0YM9rjPSso.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsIjAiOiJwYXlsb2FkIn0"
@oauth = Koala::Facebook::OAuth.new(12345, "secret") # example secret is 'secret', app ID doesn't matter
@oauth.parse_signed_request(signed_request)
# => {"algorithm"=>"HMAC-SHA256", "0"=>"payload"}
# Example request from http://developers.facebook.com/docs/authentication/canvas/encryption_proposal
# note: this support is available as of 1.0 beta
signed_request = "LONG_STRING_OF_CHARACTERS"
# dummy app ID, secret provided by Facebook
@oauth = Koala::Facebook::OAuth.new(12345, "13750c9911fec5865d01f3bd00bdf4db")
@oauth.parse_signed_request(signed_request)
# note: this will fail for you because the signed params are over an hour old, but it would produce:
# => {"iv"=>"fDLJCW-yiXmuNa24eSarJg", "payload"=>"LONG_STRING_OF_CHARACTERS", "algorithm"=>"AES-256-CBC HMAC-SHA256", "issued_at"=>1287601988}
Recommendation: instead of managing the OAuth flow on your own, use OmniAuth or another authentication framework. Using their solid implementations will let you focus on building your app rather working through codes and redirects. However, if for some reason you have or prefer to handle the redirect-based OAuth token process yourself, have no fear: Koala supports what you need.
If you're building a website, you should consider using the Javascript library as described above, which is simple, prettier than redirects, and easy to use for both you and your users. The cookie format has been stable, though it's technically open to change without notice if security issues arise.
To learn more about OAuth, check out Facebook's page on OAuth authentication at http://developers.facebook.com/docs/api#authorization.
Here's how to get an OAuth token in two steps:
- First, send your users to the OAuth code URL. Facebook verifies that they're logged in, want to access your app, and are willing to grant you any permissions you request. They then redirect the user to a callback on your registered site with an OAuth code in the GET parameters.
- Now that you have the code, you can get the token. (Don't confuse one for the other; I've made that mistake.) You can now send a request to a second OAuth URL with that code, your application's super-secret code, and some other parameters. Facebook returns the code to you in the body of the response (you should do this from your server behind-the-scenes).
Let's walk through it together. Koala makes it easy to generate the URLs you need. All you have to do is create an OAuth helper with information for your app. (callback_url is optional, but if you don't include it here, you have to include it in later calls.)
@oauth = Koala::Facebook::OAuth.new(api_key, app_secret, callback_url)
# => #<Koala::Facebook::OAuth:0x1017177b0 @app_id=#{your_app_id}, @oauth_callback_url=#{your_callback_url}, @app_secret=#{your_secret_code}>
Now we can use @oauth to generate the URL for the authentication code. url_for_oauth_code takes a hash, which can include the :permissions you want (either as an array or a comma-separated string,) and a :callback URL (in case you want to point the callback to a specific address in your domain.)
@oauth.url_for_oauth_code
# => "https://graph.facebook.com/oauth/authorize?client_id=#{app_id}&redirect_uri=#{callback_url}"
@oauth.url_for_oauth_code(:permissions => "publish_actions")
# => "https://graph.facebook.com/oauth/authorize?client_id=#{app_id}&redirect_uri=#{callback_url}&scope=publish_actions"
# you can provide your own token to protect against cross-site request forgery (CSRF)
# this is highly recommended
# see https://developers.facebook.com/docs/howtos/login/server-side-login/
@oauth.url_for_oauth_code(:permissions => "publish_actions", :state => "RANDOMSTRING")
# => "https://graph.facebook.com/oauth/authorize?client_id=#{app_id}&redirect_uri=#{callback_url}&scope=publish_stream&state=SOMERANDOMSTRING"
You can also specify a :display option to choose the form factor of the page that the user will be redirected to:
@oauth.url_for_oauth_code(:display => "touch")
# => "https://graph.facebook.com/oauth/authorize?client_id=#{app_id}&redirect_uri=#{callback_url}&display=touch"
Now that you have the URL, you can send your user there. Once she's logged into Facebook and granted your permissions (if requested), she'll be redirected to your callback; the code will be in that request's GET parameters (under the key, you guessed it, code). Now you can generate the URL that will yield up the access token.
@oauth.url_for_access_token(code)
# => "https://graph.facebook.com/oauth/access_token?client_id=#{app_id}&redirect_uri=#{callback_url}&client_secret=#{app_secret}&code=#{code}"
Of course, having a URL isn't the same as having the access code. Don't worry -- there's a method for that.
result = @oauth.fetch_token_string(code)
# => "access_token=#{access_token}&expires=#{seconds_from_now}"
@oauth.parse_token_string(result)
# => {"expires" => #{seconds_from_now}, "access_token" => #{access_token}}
Though, why do in two steps what you can do in one?
@oauth.get_access_token(code)
# => #{access_token}
# or, if you want the expiration date as well:
@oauth.get_access_token_info(code)
# => {"expires" => #{seconds_from_now}, "access_token" => #{access_token}}
In their new secure cookie format, Facebook provides an OAuth code, which Koala automatically exchanges for an access token. Because this involves a call to Facebook's servers, you should consider storing the user's access token in their session and only calling get_user_info_from_cookies when necessary (access_token not present, you discover it's expired, etc.). Otherwise, you'll be calling out to Facebook each time the user loads a page, slowing down your site. (As we figure out best practices for this, we'll update this wiki.)