bsfitw

Ruby on Rails 4 - Authentication with Facebook and OmniAuth.

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.

52 Responses
Add your response

8051
110e5b52f6c5d95b64653434cef61dae

thanks for sharing this. looking forward to implementing it.

over 1 year ago ·
8361
Photo on 2012 07 30 at 18.13

Glad you like it!

over 1 year ago ·
8394
74baee3715517a223ceeb492e85800a4

i get this error

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

<div data-role="page">
<div id="user-widget">
<% if currentuser %>
Welcome <strong><%= current
user.name %></strong>!
<%= linkto "Sign out", signoutpath, id: "sign_out" %>
<% else %>

over 1 year ago ·
8395
Photo on 2012 07 30 at 18.13

@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
over 1 year ago ·
8433
2d62e386c079f824fe7f0c8e8e83128b

Thanks, really helpful.

over 1 year ago ·
8750
Logo 4

Definitely i will try it!! thanks!

over 1 year ago ·
8751
5a8295de13dda6cc07ec65863be4cea8

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

over 1 year ago ·
8759
6d89be962aea6ca120236b264edcf2ca

Give Ryan some <3

over 1 year ago ·
9128
188538f55df8f151232958bee8610ddf

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.

over 1 year ago ·
9139
Photo on 2012 07 30 at 18.13

@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.

over 1 year ago ·
9153
188538f55df8f151232958bee8610ddf

@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.

over 1 year ago ·
9790

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

over 1 year ago ·
10068

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

over 1 year ago ·
10074
188538f55df8f151232958bee8610ddf

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!

over 1 year ago ·
10469
Dsc 1376

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

over 1 year ago ·
10717
Hrx photobooth1 copy

remember to migrate the database! heroku rake db:migrate

over 1 year ago ·
11491
E299a2906c329f741a4d8784e956285e

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!

over 1 year ago ·
11493
Photo on 2012 07 30 at 18.13

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

over 1 year ago ·
11496
E299a2906c329f741a4d8784e956285e

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!

over 1 year ago ·
11497
4cec42136c3cf713d89cf2a7fbae53c1

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!

over 1 year ago ·
11498
E299a2906c329f741a4d8784e956285e

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!

over 1 year ago ·
11723

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

over 1 year ago ·
11980
8555eec52f2da852df6cbfd2db71f306

undefined method `slice' for nil:NilClass

over 1 year ago ·
12065
B21a1e60af5fa538879df25b9118ff94

to fix error reported by @semvc you need to set

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

in facebook.js.coffescript

over 1 year ago ·
12486

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

over 1 year ago ·
12758
73f4e50f4f3af191ecaee33b27ec69fa

Practical tutorial! Thanks :)

over 1 year ago ·
12958
Ebfbfe34569dfcde2da2e93fd26b4d35

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

over 1 year ago ·
13060

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?

over 1 year ago ·
13384
9b0f7bbf22d65fa2bd23496973c0ba80

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

Will recommend! Thanks!

over 1 year ago ·
13564
934851994d52ee9f4d7dab8a5c7420c9 normal

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

over 1 year ago ·
13878
B41b0177b5daeb6c0529c7487dedc2b7

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).

over 1 year ago ·
13953
Photo on 2012 07 30 at 18.13

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

over 1 year ago ·
13955
Photo on 2012 07 30 at 18.13

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

over 1 year ago ·
13958
Photo on 2012 07 30 at 18.13

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]
over 1 year ago ·
13962
Photo on 2012 07 30 at 18.13

Stop and restart your app?

over 1 year ago ·
14382
8a388627a6519b743966d82e890b0727

When the ajax call tries to load the facebook script, I get this error: Given URL is not permitted by the application configuration.: One or more of the given URLs is not allowed by the App's settings. It must match the Website URL or Canvas URL, or the domain must be a subdomain of one of the App's domains.

I wonder why that didn't happen to anyone else. I must have missed something. Configuring the app to allow cross site ajax requests doesnt seem to help

over 1 year ago ·
14740
98be2f5c2ffc61dcc45b6b16745e599d

thanks sergiotapia but when I got to the end of step finish to test the application at localhost:3000/auth/facebook, I got this error:

{
"error": {
"message": "Invalid redirect_uri: URL \u0111\u00e3 cho kh\u00f4ng \u0111\u01b0\u1ee3c c\u1ea5u h\u00ecnh \u1ee8ng d\u1ee5ng cho ph\u00e9p.",
"type": "OAuthException",
"code": 191
}
}

the problem is the URL in the Facebook developer center, but none of the solutions I read works for me

over 1 year ago ·
15271
D8820829fe9667be24434b7a8116c966

I'm having routing error ! Can somebody help me ? What to add in the routes.rb so that I can view it

over 1 year ago ·
15376

Is there any i can do with out having to write coffee script and just jqeury?

over 1 year ago ·
15880

it is showing error uninitialized constant OmniAuth can anybody help me with it

over 1 year ago ·
16088
Bd2e7b82b29e1c831d5861be9bc5567c normal

anyone has error free working code for this on github or open source platforms?

over 1 year ago ·
16741
Cd7e457f40593b01be23dfc1441392ae

The where on User, would raise a ActiveModel::ForbiddenAttributesError,
so it should be written like this:

where(provider: auth.provider, uid: auth.uid).firstorcreate do |user|

and the controller should had:

private

def userparams
params.require(:user).permit(:provider, :uid, :name, :oauth
token, :oauthexpiresat)
end

over 1 year ago ·
16746
D1d164ddb8e9b467f644192c0f1b3dac

After running bundle install when i run user g model .....

it gives me an error.

/home/bishisht/.rvm/gems/ruby-2.1.2/gems/actionpack-4.1.1/lib/actiondispatch/routing/mapper.rb:243:in `defaultcontrollerandaction': Missing :action key on routes definition, please check your routes. (ArgumentError)
......

and more stuffs. why is this happening?

over 1 year ago ·
16988
Chamo1 normal

it gives me an error :

No route matches [GET] "/user"

over 1 year ago ·
17090
None

@leovmcosta could it have something to do with using the "||" after you're setting the variable user = User.findby(email: params[:session][:email].downcase) ?

I am assuming that "create" is the method and the first statement contains the "user =" ? If so, there is no setting the setting the omniauth statement to "user =" in the second part if the first evalutes as false.

over 1 year ago ·
20759
None

Just set this up and I am getting 'ActiveModel::ForbiddenAttributesError'

extracted source -
def sanitizeformassassignment(attributes)
if attributes.respond
to?(:permitted?) && !attributes.permitted?
raise ActiveModel::ForbiddenAttributesError
else
attributes
end

app trace -

app/models/user.rb:3:in from_omniauth' app/controllers/sessions_controller.rb:3:increate'

over 1 year ago ·
20802
None

http://awesomescreenshot.com/01f4w1to0b - This is what I'm getting after I click login with facebook link (I can click on "give app access to your profile" on facebook, but it gives me an error when I'm being redirected to callback page in my app)

over 1 year ago ·
20803
None

http://awesomescreenshot.com/01f4w1to0b - This is what I'm getting after I click login with facebook link (I can click on "give app access to your profile" on facebook, but it gives me an error when I'm being redirected to callback page in my app)

over 1 year ago ·
22899
Gg normal

thanks for your posting.
It was very helpful. but can I ask about the deleting cookie session?
When I log out, then I try to login, without entering facebook ID, Pw, It is logged in.. I hope to entering again.. help me..

over 1 year ago ·
27504
Bedf1eb084dad67519b308ac4f9c00f8

I had a ForbiddenAttributesError, this helped me: http://stackoverflow.com/a/25454227/1945990. The Rails team have added security features.

6 months ago ·