Facebook login in Rails using devise and omniauthable

みんなこんにちは!また嬉しいインターン生マフディーです!

I've learned so many new things this week and I can't wait to see what's next!

Lately, I've been working on Facebook integration in a Rails project so this time I'm going to show you how to setup Facebook login for users on your site!

In this case, we are going to use plataformatec/devise · GitHub for managing our users' authentication and various other things. Make sure to read the plataformatec/devise · GitHub page to understand how it works.

For the rest of this article, we are going to assume that we have a working devise installation with a model named 'User'.

First, start by adding the gem 'omniauth-facebook'

gem 'omniauth-facebook'

then running 'bundle install'!

Next, we have to add some columns to our database table 'users', so we run:

rails g migration AddColumnsToUsers provider uid name image
rake db:migrate

Declare your Facebook app credentials in config/initializers/devise.rb as follow:

config.omniauth :facebook, "<FB_APP_ID>", "<FB_APP_SECRET>"

then enable 'omniauthable' in your model app/models/user.rb

devise :omniauthable, :omniauth_providers => [:facebook]

Now we need to implement a callback for when the user authenticates. We do this by replacing 'devise_for :users' in config/routes.rb with

devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }

then, creating the new controller at app/controllers/users/omniauth_callbacks_controller.rb which should have the following content:

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
end

and finally, add the following in your model models/users.rb

def self.from_omniauth(auth)
  new_user = false

  facebook_user = where(auth.slice(:provider, :uid)).first_or_create do |user|
    user.email = auth.info.email
    user.password = Devise.friendly_token[0,20]
    user.name = auth.info.name
    user.image = "#{auth.info.image}?type=large"
    new_user = true
  end

  # Update already existing user
  unless new_user
    facebook_user.name = auth.info.name
    facebook_user.image = "#{auth.info.image}?type=large"
    facebook_user.save
  end

  facebook_user
end

Done and done! You only have to include the Facebook login link now!

<%= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %>

I have found plenty of resources explaining how to implement Facebook login, but most of them were unnecessarily confusing or outdated. This method should work without issues!