Skip to main content Link Search Menu Expand Document (external link)

Oauth Facebook Login

In addition to utilizing the Devise to manage user account credentials by itself, it is also highly common to integrate other user authentication systems directly, such as Google, Facebook, Yahoo, GitHub, etc. The majority of people already have accounts with these popular websites and do not need to register all over again.

In this article, we will integrate Facebook’s third-party authentication.

Table Of Contents

  1. What is Omniauth?
  2. How to install omniauth-facebook
  3. Usage

What is Omniauth?

OmniAuth is a library that standardizes multi-provider authentication for web applications. It was created to be powerful, flexible, and do as little as possible. Any developer can create strategies for OmniAuth that can authenticate users via different systems.

To utilize OmniAuth in our app, we must employ one or more strategies. These strategies are often provided as RubyGems. For a full list of these providers, please check OmniAuth’s list of strategies.

What we will integrate here in our application as our third-party authentication is Facebook.

How to install omniauth-facebook

Reminder:

Make sure that your containers are up and running.

In your gemfile, add gem 'omniauth-facebook' and gem 'omniauth-rails_csrf_protection' (for OmniAuth 2.0+ versions).

gem 'omniauth-facebook'
gem 'omniauth-rails_csrf_protection'

Then run bundle install.

 root@0122:/usr/src/app# bundle install
Fetching gem metadata from https://rubygems.org/..........
Resolving dependencies...
...
Fetching omniauth 2.1.1
Installing snaky_hash 2.0.1
Installing omniauth 2.1.1
Fetching oauth2 2.0.9
Fetching omniauth-rails_csrf_protection 1.0.1
Installing oauth2 2.0.9
Fetching omniauth-oauth2 1.8.0
Installing omniauth-rails_csrf_protection 1.0.1
Installing omniauth-oauth2 1.8.0
Fetching omniauth-facebook 9.0.0
Installing omniauth-facebook 9.0.0
Bundle complete! 30 Gemfile dependencies, 117 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

Usage

Lets add the columns provider (string), uid (string), and raw_data (text) to User model. These columns will handle the facebook authentication information. Additionally, we will also get the users facebook image, for this we need an avatar (string) and remote_avatar_url (string) columns.

 root@0122:/usr/src/app# rails g migration AddOmniauthToUsers
      invoke  active_record
      create    db/migrate/xxxxxxxxxxxxxx_add_omniauth_to_users.rb
# db/migrate/xxxxxxxxxxxxxx_add_omniauth_to_users.rb

class AddOmniauthToUsers < ActiveRecord::Migration[7.0]
  def change
    add_column :users, :uid, :string, null: false, default: ''
    add_column :users, :provider, :string, null: false, default: ''
    add_column :users, :raw_data, :text

    # for user avatar
    add_column :users, :avatar, :string
    add_column :users, :remote_avatar_url, :string
  end
end

Then migrate.

 root@0122:/usr/src/app# rails db:migrate
== xxxxxxxxxxxxxx AddOmniauthToUsers: migrating ===============================
-- add_column(:users, :uid, :string, {:null=>false, :default=>""})
   -> 0.0088s
-- add_column(:users, :provider, :string, {:null=>false, :default=>""})
   -> 0.0086s
-- add_column(:users, :raw_data, :text)
   -> 0.0089s
-- add_column(:users, :avatar, :string)
   -> 0.0100s
-- add_column(:users, :remote_avatar_url, :string)
   -> 0.0083s
== xxxxxxxxxxxxxx AddOmniauthToUsers: migrated (0.0266s) ======================

Declare the provider in config/initializers/devise.rb.

# config/initializers/devise.rb

# ...
Devise.setup do |config|
  # ...
  # ==> OmniAuth
  # Add a new OmniAuth provider. Check the wiki for more information on setting
  # up on your models and hooks.
  # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
+ config.omniauth :facebook, 'APP_ID', 'APP_SECRET'
  # ...
end

Creating Developer Facebook App.

  1. Go to Facebook Developer and login your account.
  2. Navigate to My Apps tab.
  3. Click Create App.
  4. In Select an App Type, choose Nothing then click Next.
  5. Fill out App name and App contact email then click Create. (Enter password for confirmation)
  6. Go to Settings > Basic, Copy your APP_ID and APP_SECRET.

Create a facebook yaml files to store these credentials.

# config/facebook.example.yml

development:
  secret: xxxx
  app_id: xxxx
test:
  secret: xxxx
  app_id: xxxx
# config/facebook.yml

development:
  secret: xxxx
  app_id: xxxx
test:
  secret: xxxx
  app_id: xxxx

Dont forget to exclude facebook.yml to your commits.

# .gitignore

  # ...
+ /config/facebook.yml

Input the app credentials to devise omniauth config.

# config/initializers/devise.rb

# ...
Devise.setup do |config|
  # ...
- config.omniauth :facebook, 'APP_ID', 'APP_SECRET'
+ config.omniauth :facebook, Rails.application.config_for(:facebook)[:app_id],
+                 Rails.application.config_for(:facebook)[:secret]
  # ...
end

After configuring the strategy, we need to make User model omniauthable.

# app/models/user.rb

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
-        :recoverable, :rememberable, :validatable
+        :recoverable, :rememberable, :validatable,
+        :omniauthable, omniauth_providers: %i[facebook]
  # ...
end

By declaring devise as omniauthable, this will create routes for provider we specified. Let’s check the routes that it made.

 root@0122:/usr/src/app# rails routes
                          Prefix Verb     URI Pattern                                Controller#Action
                new_user_session GET      /users/sign_in(.:format)                   devise/sessions#new
                    user_session POST     /users/sign_in(.:format)                   devise/sessions#create
            destroy_user_session DELETE   /users/sign_out(.:format)                  devise/sessions#destroy
user_facebook_omniauth_authorize GET|POST /users/auth/facebook(.:format)             devise/omniauth_callbacks#passthru
 user_facebook_omniauth_callback GET|POST /users/auth/facebook/callback(.:format)    devise/omniauth_callbacks#facebook

Now add a link for facebook login.

<!-- app/views/layouts/application.html.erb -->

    <!-- ... -->
    <% else %>
      <%= link_to 'Sign in', new_user_session_path %>
+     <%= button_to 'Sign in with Facebook', user_facebook_omniauth_authorize_path, 
+                   method: :post, data: { turbo: false } %>
    <% end %>
    <%= link_to "EN", params.permit!.merge(locale: 'en') %>
    <%= link_to "zh-CN", params.permit!.merge(locale: 'zh-CN') %>
    <%= yield %>
  </body>
</html>

The user will be routed to Facebook by clicking on the above link. They will be forwarded to your application’s callback function after entering their credentials.

To implement the callback, to go to our config/routes.rb file and tell Devise in which controller we will add Omniauth callbacks.

# config/routes.rb

Rails.application.routes.draw do
- devise_for :users
+ devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
  # ...
end

Now let’s create a controller Users::OmniauthCallbacksController, and modify its content.

# app/controllers/users/omniauth_callbacks_controller.rb

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  # See https://github.com/omniauth/omniauth/wiki/FAQ#rails-session-is-clobbered-after-callback-on-developer-strategy
  skip_before_action :verify_authenticity_token, only: :facebook

  def facebook
    # You need to implement the method below in your model (e.g. app/models/user.rb)
    @user = User.create_from_provider_data(request.env['omniauth.auth'])

    if @user.persisted?
      sign_in_and_redirect @user
      set_flash_message(:notice, :success, kind: 'Facebook') if is_navigational_format?
    else
      set_flash_message(:alert, :failure, { kind: 'Facebook', reason: @user.errors&.full_messages&.join('') })
      redirect_to new_user_registration_url
    end
  end

  def after_sign_in_path_for(resource)
    welcome_path
  end

  def failure
    redirect_to root_path
  end
end
  • OmniAuth returns all information obtained from Facebook as a hash at request.env['omniauth.auth'].
  • Once a legitimate user has been identified, they may be signed in using one of two Devise methods: sign_in or sign_in_and_redirect.
  • It is also possible to set a flash message using one of Devise’s predefined messages, although this is entirely up to you.

After we’ve established the controller, we’ll need to define the create_from_provider_data function in our user model. Every social platform have unique id for its user. We will make use of this upon user creation.

# app/models/user.rb

class User < ApplicationRecord
  # ...
  def self.create_from_provider_data(provider_data)
    find_or_create_by(provider: provider_data.provider, uid: provider_data.uid) do |user|
      user.email = provider_data.info.email
      user.password = Devise.friendly_token[0, 20]
      user.raw_data = provider_data
      user.genre = :client
    end
  end
end

Now that we are done with the callback, we need to declare on our facebook app the domain and redirect url. Given that the facebook does not allow unsafe url, we need to use ngrok and modify our applications url.

 $~> ngrok http 3000
Session Status                online
Account                       xxxxxx
Update                        update available (version 3.1.1, Ctrl-U to update)
Version                       3.0.3
Region                        Asia Pacific (ap)
Latency                       29.425332ms
Web Interface                 http://127.0.0.1:4040
Forwarding                    https://xxxx-xxx-xx-xxx-xxx.ap.ngrok.io -> http://localhost:3000
# config/environments/development.rb

require "active_support/core_ext/integer/time"

Rails.application.configure do
  # ...
+ config.hosts << 'xxxx-xxx-xx-xxx-xxx.ap.ngrok.io'
end

Now add redirect url to our facebook app:

  • Go to Facebook App, then on sidebar you will see Products, click the Add Product button beside.
  • Add Facebook Login by clicking Set Up under it.
  • Ignore the Quickstart and navigate to Facebook Login > Settings
  • Add Valid OAuth Redirect URIs:
      https://xxxx-xxx-xx-xxx-xxx.ap.ngrok.io/users/auth/facebook/callback
    
  • To make sure that your uri is valid, you can check it with Redirect URI Validator after saving the changes.

The next step is to save the Facebook profile picture. The return data request.env['omniauth.auth'] also contains facebook image url. Carrierwave offers a method where you simply need to provide the image url in the remote url field and it will take care of the storing/saving of image for you.

Mount ImageUploader to avatar column on User model. Then save the facebook image url to remote_avatar_url.

# app/models/user.rb

class User < ApplicationRecord
  # ...
  enum genre: { client: 0, admin: 1 }
+ mount_uploader :avatar, ImageUploader
  # ...
  def self.create_from_provider_data(provider_data)
    find_or_create_by(provider: provider_data.provider, uid: provider_data.uid) do |user|
      user.email = provider_data.info.email
      user.password = Devise.friendly_token[0, 20]
      user.raw_data = provider_data
      user.genre = :client
+     user.remote_avatar_url = provider_data.info.image
    end
  end
end

Then display it to welcome page.

<!-- app/views/welcome/index.html.erb -->

  <!-- ... -->
- <% if user_signed_in? %>
-   <h2> Hello <%= current_user.email %> </h2>
-   <%= link_to 'Sign out', destroy_user_session_path, data: { 'turbo-method': :delete } %>
- <% else %>
-   <%= link_to 'Sign in', new_user_session_path %>
- <% end %>
+ <br>
+ <%= image_tag current_user.avatar.url, width: 300 if current_user&.avatar&.present? %>

When you finished the setup, you may now try logging in using facebook on our application.

If you wish to login using an email other than the one used to create your Facebook App during development, you must complete one (only one) of the following steps:

  • Turn on Live Development then afterwards Get Advanced Access with public_profile (under App Review > Permissions and Features ).
  • Or add the Facebook Account you will use as Developer (under App Roles > Roles ).
  • Or create a Test User (under App Roles > Test Users ).

We are now able to login using Facebook. That’s all for Oauth Facebook Login.


Back to top

Copyright © 2020-2022 Secure Smarter Service, Inc. This site is powered by KodaCamp.