Facebook Omniauth Rails


Setup a Facebook Developer Account


Integrate Omniauth


Add Omniauth To Your Gemfile

# Gemfile
gem 'omniauth-facebook'

Update The User Table With New Columns

rails g migration AddOmniauthToUsers provider:string uid:string name:string image:text
rails db:migrate

Update Initializer

# config/initializers/devise.rb
if Rails.env.production?
  config.omniauth :facebook, "App ID", "App Secret", callback_url: "https://www.site.com/auth/facebook/callback"
else
  config.omniauth :facebook, "App ID", "App Secret", callback_url: "http://localhost:3000/auth/facebook/callback"
end

Update The Users Model

# app/models/user.rb
devise :omniauthable, :omniauth_providers => [:facebook]
# app/views/pages/home.html.slim
- unless current_user
  = link_to "Sign in with Facebook", user_facebook_omniauth_authorize_path
- else
  = link_to "Logout", destroy_user_session_path, method: :delete

Update Routes

# config/routes.rb
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }

Create a Users Directory

mkdir app/controllers/users

Create a Users Controller

# app/controller/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    @user = User.from_omniauth(request.env["omniauth.auth"])

    if @user.persisted?
      sign_in_and_redirect @user, :event => :authentication
      set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
    else
      session["devise.facebook_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end

  def failure
    redirect_to root_path
  end
end

Add Custom Methods To The User Model

# app/models/user.rb
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"] if user.email.blank?
    end
  end
end

def self.from_omniauth(auth)
  where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
    user.email = auth.info.email
    user.password = Devise.friendly_token[0,20]
    user.name = auth.info.name   # assuming the user model has a name
    user.image = auth.info.image # assuming the user model has an image
  end
end

Put App In Live Mode

In order to take your app out of development mode and available for public use, you must give a valid privacy policy url and email in Facebook Developer -> Your App -> Settings -> Basic. Then after that, save your setting and navigate to App Review and change "Make https://www.yoursite.com public?" to yes.

And woooaala! After finishing these steps, you can now log into your site using a Facebook account.

Side Note

To add more than one omniauth provider do the following. (Don't forget to create a dev app on the provider you are wanting also)

Add the gem for the provider.

# Gemfile
gem 'omniauth-github'

Add it to the devise initializer.

# config/initializers/devise.rb
if Rails.env.production?
  config.omniauth :facebook, ENV.fetch("FACEBOOK_APP_ID"), ENV.fetch("FACEBOOK_SECRET"), callback_url: "https://www.site.com/auth/facebook/callback"
  config.omniauth :github, ENV.fetch("GITHUB_CLIENT_ID"), ENV.fetch("GITHUB_SECRET")
else
  config.omniauth :facebook, ENV.fetch("FACEBOOK_APP_ID"), ENV.fetch("FACEBOOK_SECRET"), callback_url: "http://localhost:3000/auth/facebook/callback"
  config.omniauth :github, ENV.fetch("GITHUB_CLIENT_ID"), ENV.fetch("GITHUB_SECRET")
end

Add it to your routes devise scope.

# config/routes.rb
devise_scope :user do  
  get "/auth/facebook/callback" => "users/omniauth_callbacks#facebook"
  get "/auth/github/callback" => "users/omniauth_callbacks#github"
end

And then add it to your Omniauth Callback Controller.

# app/controllers/users/ominauth_callbacks_controller.rb
def github
  @user = User.from_omniauth(request.env["omniauth.auth"])

  if @user.persisted?
    sign_in_and_redirect @user, :event => :authentication
    set_flash_message(:notice, :success, :kind => "Github") if is_navigational_format?
  else
    session["devise.github_data"] = request.env["omniauth.auth"]
    redirect_to new_user_registration_url
  end
end

Add here is the route prefix for the views.

# app/views/pages/home.html.slim
- unless current_user
  = link_to "Sign in with Github", user_github_omniauth_authorize_path
- else
  = link_to "Logout", destroy_user_session_path, method: :delete