DEV Community

Mbonu Blessing
Mbonu Blessing

Posted on

Implementing Facebook authentication with Devise for your Rails 6 app

Hello everyone,

This week, I will be writing about how to setup Facebook authentication for your Rails app.

Prerequisites

  • An app using Rails 6 with devise setup for authentication
  • A working knowledge of creating a new Rails app
  • A working knowledge of devise for authentication
  • An active Facebook account

Facebook Authentication

To implement Facebook auth, we need to create an app on Facebook that will generate an APP_ID and APP_SECRET for setting up devise omniauth later.

Create Facebook app

  • Go to https://developers.facebook.com/
  • Click on My Apps in the navbar
  • Click on Add a new App Alt Text
  • On the modal that pops up, select the For Everything Else option. Alt Text
  • Next, add your App Display Name and click the Create App Id button Alt Text
  • Complete the security check and this should redirect you to the app Alt Text
  • On the sidebar, click on Setting and then click on Basic. You should be able to see your APP_ID and APP_SECRET. Copy and save for later. Alt Text
  • In the App domains, add localhost. You can also add your add icon if you have one. Alt Text
  • Scroll down to the end of the page and click on the Add Platform Alt Text
  • On the modal that pops up, select Website Alt Text
  • Add http://localhost Alt Text
  • Save your changes

Setup Facebook Auth in devise

In this section, we will be setting up facebook auth in our rails app.

  • Add the omniauth-facebook gem to your gemfile
gem 'omniauth-facebook'
Enter fullscreen mode Exit fullscreen mode
  • Run bundle install
  • Next we create a migration to add provider and uid to the user model. This stores the provider the user uses to register whether its facebook or google.
$ rails g migration AddOmniauthToUsers provider:string uid:string
Enter fullscreen mode Exit fullscreen mode
  • Run rake db:migrate
  • Next, we need to update the devise initializer file to include the APP_ID and APP_SECRET we saved for later
# config/initializers/devise.rb
config.omniauth :facebook, "APP_ID", "APP_SECRET"
Enter fullscreen mode Exit fullscreen mode

Note: If you chose to use the credentials to save your environment variables. You can use this command to edit it in your code editor. Run this code in your terminal. Change code part to your code editor if you don't use vscode.

$ `EDITOR="code --wait" bin/rails credentials:edit`
Enter fullscreen mode Exit fullscreen mode

In the decrypted credential file, add the app_id and app_secret

facebook:
  APP_ID: '<facebook_app_id>'
  APP_SECRET: '<facebook_app_secret>'
Enter fullscreen mode Exit fullscreen mode

So your initializer file update will be

config.omniauth :facebook, Rails.application.credentials.facebook[:APP_ID], Rails.application.credentials.facebook[:APP_SECRET], token_params: { parse: :json }
Enter fullscreen mode Exit fullscreen mode
  • Next we need to make the user model omniauthable. Add this to the user model
# app/models/user.rb
devise :omniauthable, omniauth_providers: %i[facebook]
Enter fullscreen mode Exit fullscreen mode
  • Next, we create an omniauth controller in a user folder to respond to facebook authentication.
# app/controllers/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 #this will throw if @user is not activated
      set_flash_message(:notice, :success, kind: "Facebook") if is_navigational_format?
    else
      session["devise.facebook_data"] = request.env["omniauth.auth"].except(:extra) # Removing extra as it can overflow some session stores
      redirect_to new_user_registration_url
    end
  end

  def failure
    redirect_to root_path
  end
end
Enter fullscreen mode Exit fullscreen mode
  • Next, we go back to the user model to add the from_omniauth self method. This method finds or creates a new user when they register. The name_split part of for those that store the first_name and last_name of their users.
# app/models/user.rb
def self.from_omniauth(auth)
  name_split = auth.info.name.split(" ")
  user = User.where(email: auth.info.email).first
  user ||= User.create!(provider: auth.provider, uid: auth.uid, last_name: name_split[0], first_name: name_split[1], email: auth.info.email, password: Devise.friendly_token[0, 20])
    user
end
Enter fullscreen mode Exit fullscreen mode
  • Next, we update the routes file for include the new controller we just added
# config/routes.rb
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
Enter fullscreen mode Exit fullscreen mode
  • Finally, we add the facebook button to our view using a facebook path provided by devise. You can use this same path for both registration and login. You just need to update the button description.
<%= link_to "Register with Facebook", user_facebook_omniauth_authorize_path %>
Enter fullscreen mode Exit fullscreen mode

And that should add facebook auth to your app.

Other Authentication

The process is quite similar for google authentication. We need to create a project for the app and then create the credentials for auth. I am going to attach a link to create credentials for google auth and the omniauth-google gem.

Create credentials on google
Omniauth Google gem
Omniauth twitter gem
Twitter guide for creating an app

Let me know what you think and what to write about in the comment section.

Until next week

Top comments (10)

Collapse
 
rhymes profile image
rhymes

Nice article Mbonu, such a coincidence BTW :-)

@joshpuetz has been working on adding optional Facebook authentication support to Forems in the following PR:

Log in with Facebook #9922

What type of PR is this? (check all applicable)

  • [ ] Refactor
  • [x] Feature
  • [ ] Bug Fix
  • [ ] Optimization
  • [ ] Documentation Update

Description

Adds support to logging into Forem with Facebook Oauth. We're only using the Omniauth Facebook provider, not Facebook's SDK. In addition: only the bare minimum amount of information is requested from Facebook, so no app review is required on their end (and we limit the amount of user data that we're asking for). Included is:

  • a new Authentication::Provider subclass
  • new environment variables
  • migrations to add fields to user
  • broadcast seed and production data
  • of COURSE tests

Related Tickets & Documents

resolves github.com/forem/InternalProjectPl...

QA Instructions, Screenshots, Recordings

DEV_local__Community_👩💻👨💻

Added tests?

  • [x] yes
  • [ ] no, because they aren't needed
  • [ ] no, because I need help

Added to documentation?

  • [x] docs.forem.com
  • [ ] readme
  • [ ] no documentation needed

[optional] Are there any post deployment tasks we need to perform?

[optional] What gif best describes this PR or how it makes you feel?

The Facebook

The main difference in our approach is that we have a "one to many" correspondence between a user and the preferred login method. Something we call Identity. Thus a user can have a Twitter identity, a GitHub identity and so on.

Collapse
 
boriscy profile image
Boris Barroso • Edited

I had some problems with this, I have to add SSL to my localhost app so it works with FB but it says the url is not part of the config I don't understand since I'm in localhost:3000 and I have added localhost as part of the domains as well as added the url localhost:3000 in Facebook

Collapse
 
truetechcode profile image
Aosu Stephen Terver

Thanks Blessing for this helpful article, If you are getting this Error: Not found. Authentication passthru after following this guide, there is a fix for it in this github issue: github.com/heartcombo/devise/issue...

Collapse
 
berkovichpavel profile image
Berkovich Pavel • Edited

Not found. Authentication passthru.
How can a solve this error?

Collapse
 
mustafah profile image
Mustafah

I used gem "omniauth", "~> 1.9.1" in Gemfile instead of newer version !

Collapse
 
malik03amit profile image
Amit Malik

After using your code for Facebook authentication, this happens every time.
Will you help me, I am new to Rails.

Not found. Authentication passthru.

Collapse
 
ndvbd profile image
ndvb

What is %i[facebook] ? is the %i a mistake?

Collapse
 
frullah profile image
Fajarullah

no, %i is the non-interpolated Array of symbols, separated by whitespace.

%i[facebook] same as [:facebook].

source: stackoverflow.com/a/47039746/4919728

Collapse
 
ndvbd profile image
ndvb

I get:

The action 'facebook' could not be found for Devise::OmniauthCallbacksController

any ideas?

Collapse
 
frullah profile image
Fajarullah

I found that is caused by the controller for :omniauth_callbacks in devise route is not specified. I solved it by specify :omniauth_callbacks controller to the new controller I created based on this article (Users::OmniauthCallbacksController).

devise_for :users, controllers: {
  omniauth_callbacks: "users/omniauth_callbacks" // HERE THE SOLUTION: specify :omniauth_callbacks controller to Users::OmniauthCallbacksController
}
Enter fullscreen mode Exit fullscreen mode