Site blog title mreza

DEVISE WITH OMNIAUTH FOR SINGLE AND MULTIPLE MODELS – RAILS 5

20/12/2016
Omniauth f1d5b77f6e5fc9adc96fdb4dc3a7d5f3 %281%29

In this post we'll describe on how to use OmniAuth in combination with Rails and Devise to support authentication of existing and new users without asking for email/password combinations.

So let us start off with the new application:


# execute from terminal
rails new oauth_example
cd oauth_example
rails g scaffold city name:string postcode:string

We need to add devise:


# Gemfile.rb
gem 'devise'

Then we'll make sure we have all the required dependencies and that our User model's all set up:



# execute from terminal
bundle install
rails generate devise:install
rails generate devise user
rake db:migrate

All our controller methods should require authentication (for this example of ours)


# app/controllers/application_controller.rb
before_action :authenticate_user!

We're gonna set the default route to be the city index page


#  config/routes.rb
root 'cities#index'

Now you can test out devise over at http://localhost:3000/

Single Model

In cases when we only need to support one model for OmniAuth (User for example) then it's quite simple and it all works reasonably well out of the box. We just need to add the appropriate OAuth strategy (Facebook in this particular case) and everything will magically work.

Omniauthable


# Gemfile.rb
gem 'omniauth-oauth2', '1.3.1'
gem 'omniauth-facebook'

We'll initialize Devise - make sure you open up a Facebook app and then add the correct APP_ID and SECRET_ID to the initializer. (the Facebook app needs to be created over here: http://developers.facebook.com/apps)

For the Facebook app to work you'll need to add a valid Oauth redirect URL under Product/Facebook Login/Settings

http://localhost:3000/users/auth/facebook/callback

In order to do this you will need to add "Settings/Basic" App Domains with the value "localhost"


# config/initializers/devise.rb
config.omniauth :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_SECRET_ID'],
                 scope: 'email',
                 info_fields: 'email'

Then we need to make our model aware of this:


# app/models/user.rb
devise :omniauthable, omniauth_providers: [:facebook]



#execute from terminal
bundle install

We then add this to the bottom of the cities index html.erb page:



#app/views/cities/index.html.erb
<% if user_signed_in? %>
  <%= link_to('Logout', destroy_user_session_path, :method => :delete) %>        
<% else %>
  <%= link_to('Login', new_user_session_path)  %>  
<% end %>

Change routes for users to:


# config/routes.rb
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }

Generate controller action


# execute from terminal
 rails g controller users/omniauth_callbacks


# app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    # You need to implement the method below in your model (e.g. app/models/user.rb)
    @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"]
      redirect_to new_user_registration_url
    end
  end

  def failure
    redirect_to root_path
  end
end

and finally we add this to our app/models/user.rb


# app/models/user.rb
 def self.new_with_session(params, session)
    super.tap do |user|
      if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
        user.email = data["email"] if user.email.blank?
      end
    end
  end

  def self.from_omniauth(auth)
    user = User.find_by('email = ?', auth['info']['email'])
    if user.blank?
       user = User.new(
         {
          provider: auth.provider,
          uid: auth.uid,
          email: auth.info.email,
          password: Devise.friendly_token[0,20]
         }
       )
       user.save!
    end
    user
  end

I'm adding the Provider and UID to the User model using the next migration. This is just a poor man's version as I am cheating a little by using only one user model. For multiple Auth providers (LinkedIn, Google+ ...) we should keep our authorizations somewhere else so we would require one more table. This is described in detail over here: http://stackoverflow.com/a/22186061/185374


# execute from terminal
rails generate migration add_oauth_fields_to_users provider:string uid:string
rake db:migrate

==This should be enough to have a working OAuth set up.== All of the code is available here: https://github.com/kodius/oauth-example-single-model

Multiple Models

When we need to support multiple models for OmniAuth, the whole thing gets a tad more complicated. The default "omniauthable" Devise way of doing things is not actually supported for multiple models (https://github.com/plataformatec/devise/issues/1047). Instead, what we need to do is use OAuth as a middleware and we need to write routes by hand to make it work.

Using middleware

This requires us to remove the :omniauthable argument from our User model(app/models/user.rb)

~~devise :omniauthable, omniauth_providers: [:facebook]~~

Remove the configuration setup from devise.rb


#config/initializers/devise.rb
#config.omniauth :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_SECRET_ID'],
#                 scope: 'email',
#                 info_fields: 'email'

Create a new file called oauth.rb in the /initializers folder


# config/initializers/oauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :facebook,
           ENV['FACEBOOK_APP_ID'],
           ENV['FACEBOOK_SECRET_ID'],
           scope: 'email',
           info_fields: 'email',
           auth_type: 'rerequest'

  configure do |config|
    config.path_prefix = '/users/auth'
  end
end

Remove the Devise routes from config/routes.rb

devisefor :users~~, controllers: { omniauthcallbacks: 'users/omniauth_callbacks' }~~

Now it should look like this:


# config/routes.rb
devise_for :users

Add a manual route:


# config/routes.rb
devise_scope :user do
  get "/users/auth/facebook/callback" => "users/omniauth_callbacks#facebook"
end

We need to add a "Facebook" button on our Sign Up page. Let's generate the Devise Views so we can modify them:


#execute in terminal
rails generate devise:views

Put Facebook the button just before last line <%= render "devise/shared/links" %>


# app/views/devise/sessions/new.html.erb
# put these three lines
<%= link_to('/users/auth/facebook', {:class => "btn btn-primary"}) do %>
  Facebook OAuth
<%- end %>
<br/>

# before this line
<%= render "devise/shared/links" %>

One more thing we need to do is we have to gracefully handle failure. We'll do this by adding the on_failure block


# config/initializers/oauth.rb
on_failure do |env|
  #we need to setup env
  if env['omniauth.params'].present
    env["devise.mapping"] = Devise.mappings[:user]
  end
  Devise::OmniauthCallbacksController.action(:failure).call(env)
end

This leaves us with a setup that can be easily extended to multiple models merely by adding some routes and handlers. Since we're relying on middleware here and this means we have full control over the entire configuration and making everything work ought to be fairly straightforward. We simply need to do the same thing we've already done thus far and replace "user" with the respective model that we're adding (e.g. "employer_user" or whatever it is that we're using).

==The full working example with middleware OmniAuth is available over here:https://github.com/kodius/oauth-example-multiple-models ==

Testing

When testing this, we can't really use Facebook so what we do instead is we mock Facebook's response.


module OmniauthMacros
  def mock_auth_hash
    OmniAuth.config.mock_auth[:default] = OmniAuth::AuthHash.new(
      'provider' => 'facebook',
      'uid' => '123545',
      'info' => {
        'name' => 'mockuser',
        'image' => 'mock_user_thumbnail_url',
        'first_name' => 'john',
        'last_name' => 'doe',
        'email' => 'john@doe.com',
        'urls' => {
          'public_profile' => 'http://test.test/public_profile'
        }
      },
      'credentials' => {
        'token' => 'mock_token',
        'secret' => 'mock_secret'
      },
      'extra' => {
        'raw_info' => '{"json":"data"}'
      }
    )
  end
end

In RSpec we do this before running our tests:


RSpec.feature 'Facebook login management', type: :feature do
  before(:each) do
    OmniAuth.config.test_mode = true
    mock_auth_hash
  end
  scenario 'Should login over facebokok do
    visit '/users/sign_up'
    click_on 'Facebook OAuth'
    #.....
  end
end

This should be everything you need to successfully use OAuth in your Rails application.

[^n]: Devise Getting Started - https://github.com/plataformatec/devise#getting-started [^n]: Omniauth Overview - https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview [^n]: Multiple auth providers - http://stackoverflow.com/a/22186061/185374 [^n]: OmniAuth with multiple models - https://github.com/plataformatec/devise/wiki/OmniAuth-with-multiple-models [^n]: How to Use Omniauth to Authenticate your Users - https://code.tutsplus.com/articles/how-to-use-omniauth-to-authenticate-your-users--net-22094 [^n]: Rails Casts - Simple OmniAuth - http://railscasts.com/episodes/241-simple-omniauth [^n]: Rails 4, Devise, Omniauth (with multiple providers) - http://stackoverflow.com/a/22186061/185374

Category: Rails
OTHER POSTS
OTHER POSTS
Created with Sketch.
Railsios
06/06/2018 / Tin Ilijaš

UPLOAD IMAGE FROM SWIFT 3 IOS APP TO RAILS 5 SERVER

Once upon a time I was given the task to send an image captured on IOS to our Rails back-end server and at first I figured: “Wow this can’t be such a big deal, I know how to take an image and I know how to make a request so why would thi...
Conferencecam group
29/06/2018 / Krešimir Bojčić

LOGITECH GROUP – REMOTE VIDEO CONFERENCING SYSTEM REVIEW

To have a successful remote agency/client relationship you need three main ingredients: 1. Trust 2. Effective communication 3. Delivering results
Created with Sketch.
Global infra 3.30.18.b559f46825615c1ae40f319d0c4d9139fea9c492
29/06/2018 / Krešimir Bojčić

SCALE FOR SPEED AND AVAILABILITY

In this post I'll go over various options for scaling your business web platform. We'll take a look at five different approaches. There is no wrong or right approach, it is just a matter of what aspects you want to emphasize and what you...
Created with Sketch.
Railsandreact
28/06/2018 / Matija Munjaković

SETTING UP REACT & RAILS HAS NEVER BEEN EASIER

Just a couple of days ago Beta 1 of Rails 5.1 got released, bringing with it a slew of new features. Most prominent among them being the inclusion of Webpack. For the uninitiated, Webpack serves as a bundling tool for your project’s Java...
Untitled
03/07/2018 / Krešimir Bojčić

FREE SSL ON AWS OPSWORKS RAILS APP

In this post I'll try and describe how to set up SSL for your Rails app. This solution is free and will automatically extend the certificate once the certificate runs its course.
Created with Sketch.
23 512
06/06/2018 / Tin Ilijaš

HOW TO DEACTIVATE USER – RAILS WITH DEVISE

Sometimes when I find the time (which happens roughly 23 to 37 times each and every day) I visit Stack Overflow, hopefully to solve a problem – but regularly leave irritated and heartbroken because the solution is too geeky and doesn’t e...
Created with Sketch.
7e6dcc68 1ba3 11e7 8286 407dd1a1b50f
29/06/2018 / Tin Ilijaš

SWITCH USERS WITH USERSWITCH

Recently we were hired by a startup and quickly assigned a task to build a brand new feature for them. So no problemo here! The feature was fairly extensive which necessitated thorough testing – perhaps a third of the entire application ...
Untitled
03/07/2018 / Krešimir Bojčić

HOW TO BUILD AN MVP

So, you’ve decided to build a new disruptive product that will earn you glory and money? Congratulations… welcome to the club and good luck!
Created with Sketch.
1 ee4irlninahubiurfqv3fq
29/06/2018 / Tin Ilijaš

8 TIPS TO BECOME A DEVELOPER

Almost a year has flown by ever since I started learning programming from scratch. As a result of that, I am writing this blog post to help new and old developers with some tips I learned in this beautiful world of programming.
Created with Sketch.
Untitled
03/07/2018 / Krešimir Bojčić

VISUALIZE DATABASE SCHEMA – POSTGRESQL DB – RAILS 5

When working on bigger systems it helps having your database model visualized. There are many great solutions but a few of them are free. If you fit in the “small enough” not to use [ER/Studio Data Architect](https://www.idera.com/er-...
Helpprotectcompanydata large
29/06/2018 / Krešimir Bojčić

PROTECT THE DATA!

If you were to ask yourself – what is the essence of any software system – what would you say? (that is, what would you have said without having read the title of this post 😃).
Created with Sketch.
Hmawawbapi
03/07/2018 / Matija Munjaković

HANDLING MOBILE AUTHENTICATION WITH A WEB-BASED API

Quite recently I had the chance of implementing a log-in/authentication system for one of our mobile applications. Resources available for both the Android and iOS eco-systems are fairly exhaustive but comparisons of the pros and cons of...
Created with Sketch.
Railsios
06/06/2018 / Tin Ilijaš

UPLOAD IMAGE FROM SWIFT 3 IOS APP TO RAILS 5 SERVER

Once upon a time I was given the task to send an image captured on IOS to our Rails back-end server and at first I figured: “Wow this can’t be such a big deal, I know how to take an image and I kno...
Created with Sketch.
Conferencecam group
29/06/2018 / Krešimir Bojčić

LOGITECH GROUP – REMOTE VIDEO CONFERENCING SYSTEM REVIEW

To have a successful remote agency/client relationship you need three main ingredients: 1. Trust 2. Effective communication 3. Delivering results
Created with Sketch.
Global infra 3.30.18.b559f46825615c1ae40f319d0c4d9139fea9c492
29/06/2018 / Krešimir Bojčić

SCALE FOR SPEED AND AVAILABILITY

In this post I'll go over various options for scaling your business web platform. We'll take a look at five different approaches. There is no wrong or right approach, it is just a matter of what as...
Created with Sketch.
Railsandreact
28/06/2018 / Matija Munjaković

SETTING UP REACT & RAILS HAS NEVER BEEN EASIER

Just a couple of days ago Beta 1 of Rails 5.1 got released, bringing with it a slew of new features. Most prominent among them being the inclusion of Webpack. For the uninitiated, Webpack serves as...
Created with Sketch.
Untitled
03/07/2018 / Krešimir Bojčić

FREE SSL ON AWS OPSWORKS RAILS APP

In this post I'll try and describe how to set up SSL for your Rails app. This solution is free and will automatically extend the certificate once the certificate runs its course.
Created with Sketch.
23 512
06/06/2018 / Tin Ilijaš

HOW TO DEACTIVATE USER – RAILS WITH DEVISE

Sometimes when I find the time (which happens roughly 23 to 37 times each and every day) I visit Stack Overflow, hopefully to solve a problem – but regularly leave irritated and heartbroken because...
Created with Sketch.
7e6dcc68 1ba3 11e7 8286 407dd1a1b50f
29/06/2018 / Tin Ilijaš

SWITCH USERS WITH USERSWITCH

Recently we were hired by a startup and quickly assigned a task to build a brand new feature for them. So no problemo here! The feature was fairly extensive which necessitated thorough testing – pe...
Created with Sketch.
Untitled
03/07/2018 / Krešimir Bojčić

HOW TO BUILD AN MVP

So, you’ve decided to build a new disruptive product that will earn you glory and money? Congratulations… welcome to the club and good luck!
Created with Sketch.
1 ee4irlninahubiurfqv3fq
29/06/2018 / Tin Ilijaš

8 TIPS TO BECOME A DEVELOPER

Almost a year has flown by ever since I started learning programming from scratch. As a result of that, I am writing this blog post to help new and old developers with some tips I learned in this b...
Created with Sketch.
Untitled
03/07/2018 / Krešimir Bojčić

VISUALIZE DATABASE SCHEMA – POSTGRESQL DB – RAILS 5

When working on bigger systems it helps having your database model visualized. There are many great solutions but a few of them are free. If you fit in the “small enough” not to use [ER/Studio D...
Created with Sketch.
Helpprotectcompanydata large
29/06/2018 / Krešimir Bojčić

PROTECT THE DATA!

If you were to ask yourself – what is the essence of any software system – what would you say? (that is, what would you have said without having read the title of this post 😃).
Created with Sketch.
Hmawawbapi
03/07/2018 / Matija Munjaković

HANDLING MOBILE AUTHENTICATION WITH A WEB-BASED API

Quite recently I had the chance of implementing a log-in/authentication system for one of our mobile applications. Resources available for both the Android and iOS eco-systems are fairly exhaustive...
Created with Sketch. Created with Sketch.
Railsios
06/06/2018 / Tin Ilijaš

UPLOAD IMAGE FROM SWIFT 3 IOS APP TO RAILS 5 SERVER

Once upon a time I was given the task to send an image captured on IOS to our Rails back-end server and at first I figured: “Wo...
Created with Sketch. Created with Sketch.
Conferencecam group
29/06/2018 / Krešimir Bojčić

LOGITECH GROUP – REMOTE VIDEO CONFERENCING SYSTEM REVIEW

To have a successful remote agency/client relationship you need three main ingredients: 1. Trust 2. Effective communication...
Created with Sketch. Created with Sketch.
Global infra 3.30.18.b559f46825615c1ae40f319d0c4d9139fea9c492
29/06/2018 / Krešimir Bojčić

SCALE FOR SPEED AND AVAILABILITY

In this post I'll go over various options for scaling your business web platform. We'll take a look at five different approache...
Created with Sketch. Created with Sketch.
Railsandreact
28/06/2018 / Matija Munjaković

SETTING UP REACT & RAILS HAS NEVER BEEN EASIER

Just a couple of days ago Beta 1 of Rails 5.1 got released, bringing with it a slew of new features. Most prominent among them ...
Created with Sketch. Created with Sketch.
Untitled
03/07/2018 / Krešimir Bojčić

FREE SSL ON AWS OPSWORKS RAILS APP

In this post I'll try and describe how to set up SSL for your Rails app. This solution is free and will automatically extend th...
Created with Sketch. Created with Sketch.
23 512
06/06/2018 / Tin Ilijaš

HOW TO DEACTIVATE USER – RAILS WITH DEVISE

Sometimes when I find the time (which happens roughly 23 to 37 times each and every day) I visit Stack Overflow, hopefully to s...
Created with Sketch. Created with Sketch.
7e6dcc68 1ba3 11e7 8286 407dd1a1b50f
29/06/2018 / Tin Ilijaš

SWITCH USERS WITH USERSWITCH

Recently we were hired by a startup and quickly assigned a task to build a brand new feature for them. So no problemo here! The...
Created with Sketch. Created with Sketch.
Untitled
03/07/2018 / Krešimir Bojčić

HOW TO BUILD AN MVP

So, you’ve decided to build a new disruptive product that will earn you glory and money? Congratulations… welcome to the clu...
Created with Sketch. Created with Sketch.
1 ee4irlninahubiurfqv3fq
29/06/2018 / Tin Ilijaš

8 TIPS TO BECOME A DEVELOPER

Almost a year has flown by ever since I started learning programming from scratch. As a result of that, I am writing this blog ...
Created with Sketch. Created with Sketch.
Untitled
03/07/2018 / Krešimir Bojčić

VISUALIZE DATABASE SCHEMA – POSTGRESQL DB – RAILS 5

When working on bigger systems it helps having your database model visualized. There are many great solutions but a few of them...
Created with Sketch. Created with Sketch.
Helpprotectcompanydata large
29/06/2018 / Krešimir Bojčić

PROTECT THE DATA!

If you were to ask yourself – what is the essence of any software system – what would you say? (that is, what would you have sa...
Created with Sketch. Created with Sketch.
Hmawawbapi
03/07/2018 / Matija Munjaković

HANDLING MOBILE AUTHENTICATION WITH A WEB-BASED API

Quite recently I had the chance of implementing a log-in/authentication system for one of our mobile applications. Resources av...