Adding Facebook auth to Rails 3.1 app
20 Jan 2012
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 UserDon't forget to follow the manual configuration steps after
rails generate devise:installAdd this
before_filterin your protected controller
before_filter :authenticate_user!
Adding Facebook authentication
Add OmniAuth OAuth to our application, in our Gemfilegem '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 :omniauthableRestart 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.
