devise_facebook_open_graph

The goal of this gem is to give Devise the ability to authorize users based if they are connected to Facebook or not. It relays on connection to Facebook set up via the Facebook JavaScript SDK. Maybe devise facebook js sdk connectable should be a better name for this gem ;-)

Warning

I guess I should start with a small warning: Right now, this gem is kinda a fast-written prototype for me. Don’t expect anything to work ;-) ..Even though committed code do work for me in my Rails 3 beta 3 application :-) I have managed to authenticate users which sign in via Facebook’s JavaScript SDK and I guess this code might be useful to others too. Sadly, I haven’t written any specs while writing this code. This is not 100% complete code either. For instance, creating users based on facebook connection is not finished, although I guess it will be soon.

How to get started

Set up gem dependency in your Gemfile

gem ‘devise_facebook_open_graph’

Get facebook credentials, add ids and keys to a configuration file

If you haven’t done so already, create a facebook application so you’ll acquire an application id, secret and an API key. You can register applications here: www.facebook.com/developers/.

DeviseFacebookOpenGraph provides a class for easy access to configuration values. These are by default read from Rails.root/config/facebook.yml which should look something like this:

production: &production
  application_id: ""
  api_key: ""
  application_secret: ""

development:
  <<: *production

Configure your model to use facebook_open_graph_authenticatable

class User < ActiveRecord::Base
  devise :database_authenticatable, :facebook_open_graph_authenticatable # etc..

Add facebook user id column to your users table

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      # ...other definitions here..

      t.facebook_open_graph_authenticatable
    end

    add_index :users, User.facebook_uid_field, :unique => true
  end

  def self.down
    drop_table :users
  end
end

Add Facebook SDK initializer HTML and JavaScript

The next thing we’ll be doing are adding Facebook’s JavaScript SDK and initialize it. This should be added at the bottom of the page, before </body>. Add it with the helper facebook_init_javascript_sdk. I guess more options to this one will come later.

Add Facebook login button to your page

This can be done in different ways. One way is to use the fbml tag fb:login-button. See developers.facebook.com/docs/authentication/, single sign-on section for more information.

Last step…

Once a user has clicked your login button a cookie will be set with name fbs_<your_application_id>. When this is set and Warden tries the authentication strategy which this gem provides it will parse the cookie content, find the user’s facebook uid and look that up in your users’ table. If found, the user is authenticated.

How to sign out users?

Facebook connected users are signed out when they sign out of Facebook. You can log a user out by calling FB.logout() from your JavaScript. Here are some lines of JavaScript (jQuery) which I use to sign in/out users. Hope it helps you out if you are stuck:

$("a#sign_out").click(function(e) {
    var sign_out_link = $(this);

    FB.getLoginStatus(function(response) {
        if (response.session) { // User is connected to Facebook
            e.preventDefault();

            FB.logout(function() { // Signs the user out and redirect to the application's sign out url
                window.location.href = sign_out_link.attr("href");
            });
        }
    });
});

FB.Event.subscribe('auth.login', function(response) {
    window.location.href = "/profile"; // Redirect to some url when the user gets connected to facebook
});

For more information take a look at developers.facebook.com/docs/reference/javascript/FB.getLoginStatus Oh! ..and you might want to start here: developers.facebook.com/docs/reference/javascript/

Callbacks

There are defined some callback methods for devise models which are set up to be facebook_open_graph_authenticatable. When these methods are called the model should have access to the facebook session through self. Just make a call to facebook_session on your model. With this session you have access to uid, open graph api on behalf of the user etc. Take a look at DeviseFacebookOpenGraph::Facebook::Session and the Koala gem which we use for communicating with the open graph.

before/after/around create_by_facebook

This one is called when a new user is created based on facebook connection, but the facebook uid is now found in the database. Note that this is only relevant if devise’s configuration value facebook_auto_create_account is set to true. Oh, one more thing, the user is as a default saved without running validations meaning that you should implement a before_create_by_facebook function and extract what you can like email etc from the facebook_session. You can override this though via configuration key ‘run_validations_when_creating_facebook_user’.

Simple example on how to extract user’s data from Facebook

class User < ActiveRecord::Base
  before_create_by_facebook :extract_user_data_from_facebook_session

  def extract_user_data_from_facebook_session
    # The following code returns hash with information about current user being created
    # For more information, see Koala gem documentation http://wiki.github.com/arsduo/koala/graph-api
    user_data_from_facebook = facebook_session.graph.get_object(:me)
    # Assign attributes with fetched data..
  end
end

before/after/around connecting_to_facebook

These are called when Devise authenticates the user via facebook connect. In effect the user will be connected to facebook at this point; signing out of facebook means signing out of your application too and visa-versa. As you might expect from a before filter; you can halt the connection to facebook in the before filter by making it return false.

Configuration

There are some configuration options which you can add in config/initializers/devise.rb

# Overrides the default column name for where to store the user's facebook user id
#config.facebook_uid_field = "facebook_uid"

# Auto creates accounts for new users. Default is true
#config.facebook_auto_create_account = true

# Runs validation when auto creating users on facebook connect. Default is false
#config.run_validations_when_creating_facebook_user = false

# Skip confirmation loop on facebook connection users. Default is true
#config,skip_confimation_for_facebook_users = true

Access facebook session through controllers and views

When a user is signed in via facebook connect you have access to the user’s facebook session by calling facebook_session inside a controller or view. With this session you have access to the user’s open graph by calling facebook_session.graph. See the simple example on how to extract user’s data from Facebook above. facebook_session will return nil if we where unable to parse the cookie set by Facebook’s JavaScript SDK or if it hasn’t been set at all.

TODO

  • Add other controller helpers for easy access to users facebook session (more specifically access to user’s session & open graph).

  • Add support for non-JavaScript authentication (see developers.facebook.com/docs/authentication/ “Authenticating Users in a Web Application”).

  • I’m sure there are more things to do..

  • ..For instance; I guess some testing should be nice :-]

Note on Patches/Pull Requests

  • Fork the project.

  • Make your feature addition or bug fix.

  • Add tests for it. This is important so I don’t break it in a future version unintentionally.

  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)

  • Send me a pull request. Bonus points for topic branches.

Copyright © 2010 Thorbjørn Hermansen. See LICENSE for details.