William Notowidagdo Kiranatama Staff
Knowledge


We will learn how to add Facebook auth to an existing Rails 3.1 app. I assume that you want to use Devise as authentication solution so first of all we will add Devise to our app.

Devise setup

$ gem install devise
$ rails generate devise:install
$ rails generate devise User
Don't forget to follow the manual configuration steps after
rails generate devise:install
Add this
before_filter
in your protected controller
before_filter :authenticate_user!

Adding Facebook authentication

Add OmniAuth OAuth to our application, in our Gemfile
gem 'omniauth-facebook'
Open up config/initializers/devise.rb and add this snippet
require "omniauth-facebook"
config.omniauth :facebook, "APP_ID", "APP_SECRET"
Change APP_ID and APP_SECRET with your actual Facebook app App ID and App secret. Then add :omniauthable to your User model
devise :omniauthable
Restart your Rails app then go to the login page and you will see 'Sign in with Facebook' link there. You need to implement the callback because the user will be redirected after a succesful Facebook authentication. In your routes.rb, tell Devise which controller will implement Omniauth callbacks:
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
Now create app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    @user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)

    if @user.persisted?
      flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
      sign_in_and_redirect @user, :event => :authentication
    else
      session["devise.facebook_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end
end
From our newly created controller we can see that information from Facebook is available in request.env["omniauth.auth"]. If the user is valid we should sign it otherwise we redirect the user back to our registration form. Now in models/user.rb we need to implement
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
  data = access_token.extra.raw_info
  if user = User.where(:email => data.email).first
    user
  else # Create a user with a stub password. 
    User.create!(:email => data.email, :password => Devise.friendly_token[0,20]) 
  end
end
The find_for_facebook_oauth method tries to find an existing user by e-mail otherwise create one with a random password. Next we need to implement new_with_session in models/user.rb to copy data from session whenever a user is initialized before sign up
def self.new_with_session(params, session)
  super.tap do |user|
    if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
      user.email = data["email"]
    end
  end
end
That's it, now users could use their Facebook account to have them signed in to your app. We put the sample code for this post on our public repository, find it here.