Where developers come to connect, share, build and be inspired.

113

Ruby on Rails 4 - Authentication with Facebook and OmniAuth.

18866 views

In this article I'm going to teach you how to authenticate your users easily using Facebook. This means a much easier sign up process for your users and you no longer have the responsibility of securing passwords.

This article is written specifically for Ruby on Rails 4. Since I had to cobble together solutions from many sources hopefully this helps people get a working codebase quickly.


Objectives:

Here's a small list of the things we're going to work on. Ideally, you want to follow this progression path to get a working final result.

  • Add Omniauth and Omniauth-Facebook gems.
  • Create User model.
  • Create Sessions controller.
  • Create Omniauth initializer.
  • Add client-side functionality using javascript.
  • Showing the logged in user in the View.

Add Omniauth and Omniauth-Facebook gems.

Open up your Gemfile and add the following gems to it.

gem 'omniauth'
gem 'omniauth-facebook', '1.4.0'

Make sure you run the bundle install command. This configures your Rails application to use those gems.


Create User model.

We need to generate a User model and add in some necessary fields that Omniauth uses for user authentication.

Run the following command from terminal:

rails g model User provider uid name oauth_token oauth_expires_at:datetime

Create a method in the User model that interacts with Omniauth's object.

class User < ActiveRecord::Base
  def self.from_omniauth(auth)
    where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
      user.provider = auth.provider
      user.uid = auth.uid
      user.name = auth.info.name
      user.oauth_token = auth.credentials.token
      user.oauth_expires_at = Time.at(auth.credentials.expires_at)
      user.save!
    end
  end
end

Then we need to create a helper method in our ApplicationController to access whoever is logged in at the moment.

class ApplicationController < ActionController::Base
  protect_from_forgery

  private
  def current_user
    @current_user ||= User.find(session[:user_id]) if session[:user_id]
  end
  helper_method :current_user
end

Create Sessions controller

Next lets create a sessions controller to handle callbacks. This is where we'll actually login our users.

class SessionsController < ApplicationController
  def create
    user = User.from_omniauth(env["omniauth.auth"])
    session[:user_id] = user.id
    redirect_to root_url
  end

  def destroy
    session[:user_id] = nil
    redirect_to root_url
  end
end

Create a couple of paths in your routes.rb file as well. Otherwise your Rails application won't know what controller to use.

match 'auth/:provider/callback', to: 'sessions#create', via: [:get, :post]
match 'auth/failure', to: redirect('/'), via: [:get, :post]
match 'signout', to: 'sessions#destroy', as: 'signout', via: [:get, :post]

Create Omniauth initializer

We need to create an initializer for Omniauth. Create it in config/initializers/omniauth.rb.

OmniAuth.config.logger = Rails.logger

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :facebook, 'YOUR-APP-ID-HERE', 'YOUR-APP-SECRET-HERE'
end

Add client-side functionality using Javascript

Create a coffescript file called facebook.js.coffee.

jQuery ->
  $('body').prepend('<div id="fb-root"></div>')

  $.ajax
    url: "#{window.location.protocol}//connect.facebook.net/en_US/all.js"
    dataType: 'script'
    cache: true


window.fbAsyncInit = ->
  FB.init(appId: 'YOUR-APP-ID', cookie: true)

  $('#sign_in').click (e) ->
    e.preventDefault()
    FB.login (response) ->
      window.location = '/auth/facebook/callback' if response.authResponse

  $('#sign_out').click (e) ->
    FB.getLoginStatus (response) ->
      FB.logout() if response.authResponse
    true

Showing the logged in user in the View.

In your application.html.erb file, we'll write in some code to display either a sign in link, or the currently logged in user's name.

<div id="user-widget">
  <% if current_user %>
    Welcome <strong><%= current_user.name %></strong>!
    <%= link_to "Sign out", signout_path, id: "sign_out" %>
  <% else %>
    <%= link_to "Sign in with Facebook", "/auth/facebook", id: "sign_in" %>
  <% end %>
</div>

And there you have it.

Quick and painless Facebook authentication in a Ruby on Rails 4 application. Make sure you read Facebook's official documentation on how to request more permissions from users if you need more personal information such as emails and sex.

Comments

  • Blank-mugshot
    rachid

    Don't forget to pass some credits to our friend ryan bates. http://railscasts.com/episodes/360-facebook-authentication

  • Blank-mugshot
    transforme

    thanks for sharing this. looking forward to implementing it.

  • Blank-mugshot
    maxcnunes

    Thanks, really helpful.

  • Blank-mugshot
    txant

    @sergiotapia: Thanks for your tutorial and reply message. My error is solved. Because of my careless, I just copied and pasted the facebook.js.coffee and I didn't check the codes which along inside. The AppID need to add at the one of the lines of codes of that file. Now, my rails app works correctly.

  • Photo_on_2012-07-30_at_18.13
    sergiotapia

    Hi @semvc, you need to create that file manually using your editor of choice. :)

  • Photo_on_2012-07-30_at_18.13
    sergiotapia

    Glad you like it!

  • Blank-mugshot
    manuel-cepeda

    i get this error

    undefined local variable or method `current_user' for #<#Class:0x007f5b1c4d0ac0:0x00000003a2be00>

    <% if currentuser %> Welcome <%= currentuser.name %>! <%= linkto "Sign out", signoutpath, id: "sign_out" %> <% else %>

  • Photo_on_2012-07-30_at_18.13
    sergiotapia

    @manuel-cepada:

    Sorry about that! I forgot to mention that you need to create a helper method called current_user.

    class ApplicationController < ActionController::Base
      protect_from_forgery
    
      private
      def current_user
        @current_user ||= User.find(session[:user_id]) if session[:user_id]
      end
      helper_method :current_user
    end
    

  • Logo-4
    erikwco

    Definitely i will try it!! thanks!

  • Blank-mugshot
    tvdeyen

    Give Ryan some <3

  • Blank-mugshot
    txant

    I created the app to get the App ID and App Secret code at the facebook developer sections. But after finished all steps as u shown, I got below error. What can it be?

    Error Accessing App We're sorry, but the application you're trying to use doesn't exist or has been disabled.

  • Photo_on_2012-07-30_at_18.13
    sergiotapia

    @txant: That's probably something wrong on Facebook's end. I've never seen that message but it's pretty straight forward. Your app has been disabled or doesn't exist. Make sure you're using the correct AppID and that your app on developers.facebook.com isn't disabled.

  • 874080c31464725cc629df38ea44bc46_normal
    farantesrod

    thanks for the tutorial.

    I ran into trouble deploying in Heroku because it's required to add in the omniauth.rb initializer :

    {:clientoptions => {:ssl => {:capath => "/etc/ssl/certs"}}} # Modify this with your SSL certificates path

  • L_normal
    atdeniel

    It works! Thanks! How about with Google+? :P?comment=WORKS! THANKS! :P How about google+?

  • Blank-mugshot
    txant

    I have the error of Invalid redirect_uri on calling with ip address from others devices which located in same local network. Its okay at localhost but also have error at 127.0.0.1. Please show me the way to solve. Thanks!

  • Blank-mugshot
    thiyagarajan

    Thanks a lot!, its working well in rails 4.

  • 1ff1d726ce50602dce008417145df263_normal
    cleechtech

    remember to migrate the database! heroku rake db:migrate

  • Blank-mugshot
    semvc

    Hi there,

    having a problem with creating an initializer for Omniauth. Should you make the file omniauth.rb by yourself or should you generate it by typing in a code in your command prompt with ruby on rails...

    Thanks in advance!

  • Blank-mugshot
    semvc

    Thanks sergiotapia for your fast respond, but i have a next question...

    I've manually made the omniauth.rb file and placed the correct code into it but when trying to migrate the database --> rake db:migrate, i get the following error:

    rake aborted! uninitialized constant OmniAuth::OmniAuth...

    Maybe you know the answer?

    Thanks in advance!

  • Blank-mugshot
    leovmcosta

    Hi,

    I'm trying to implement this tutorial along the app I created following The Rails Tutorial by Michael Hartl however I'm getting an error when I try to join both codes in my create action. This is the part of my session controller which is currently giving a error:

    def create user = User.findby(email: params[:session][:email].downcase) || User.fromomniauth(env["omniauth.auth"])

    Any suggestions on how can I manage to solve this problem? Thanks!

  • Blank-mugshot
    semvc

    Hi, solved my previous problem... I'm able to log in with facebook, but when click i get the following error -->

    OmniAuth::Strategies::Facebook::NoAuthorizationCodeError

    must pass either a code parameter or a signed request (via signed_request parameter or a fbsr_XXX cookie)

    Did someone also have this problem? I've searched the internet for some hours but couldn't find any solution... :'(

    Hope someone can help me...Thanks!

  • Blank-mugshot
    madhu sudhan

    Thanks its helped me , And please make facebook.js.coffee file name to facebook.js.coffee.erb

  • Blank-mugshot
    vickoman86

    undefined method `slice' for nil:NilClass

  • Blank-mugshot
    nunziofiore

    to fix error reported by @semvc you need to set

      window.location = '/auth/facebook' if response.authResponse
    

    in facebook.js.coffescript

  • Goofy_normal
    sameera207

    Thanks for the great post, got it working, however I had to remove 'e.preventDefault()' part in 'facebook.js.coffee' for it to work :)

  • Blank-mugshot
    jojas

    Practical tutorial! Thanks :)

  • Blank-mugshot
    folivi

    Koala is also a great gem for fb. Haven't tried it yet on Rails4 apps but it''s worth ckecking out

  • Blank-mugshot
    christophe1

    Just reading through it now and typing in your code, not tested yet...looks good but could be clearer for the novice user like:

    'Create a coffescript file called facebook.js.coffee'

    Where? I take it in the App/assets/javascripts folder?

  • Blank-mugshot
    sebastialonso

    Succinct, very quick to implement, and most importantly, NO DEVISE!

    Will recommend! Thanks!

  • 934851994d52ee9f4d7dab8a5c7420c9_normal
    martinvanaken

    Work as advised. Impressive as this can quickly become complicated stuff. Much appreciated.

  • Blank-mugshot
    lovettlovett

    Thanks for the great tutorial! I followed your process to a "T", but I'm getting a similar error as @leovmcosta. I'm getting this error in my SessionsController create function, "syntax error, unexpected end-of-input, expecting keyword_end" (http://cl.ly/image/3a0q3k0R3N1k).

  • Photo_on_2012-07-30_at_18.13
    sergiotapia

    @mcmaur: That means that you didn't create the SessionsController. Check above to see what it needs to have.

  • Photo_on_2012-07-30_at_18.13
    sergiotapia

    Notice "Session" vs. "Sessions" - they're not the same. :) Check your routes.rb file.

  • Photo_on_2012-07-30_at_18.13
    sergiotapia

    Keep it this way:

    class SessionsController < ApplicationController
    end
    

    Create a couple of paths in your routes.rb file as well. Otherwise your Rails application won't know what controller to use.

    match 'auth/:provider/callback', to: 'sessions#create', via: [:get, :post]
    match 'auth/failure', to: redirect('/'), via: [:get, :post]
    match 'signout', to: 'sessions#destroy', as: 'signout', via: [:get, :post]
    

  • Photo_on_2012-07-30_at_18.13
    sergiotapia

    Stop and restart your app?

  • Gkp3yhuq_normal
    mcmaur

Add a comment