Ruby on Rails

Ruby on Rails / Rubygems – Devise and LinkedIn authentication

Ruby on Rails / Rubygems – Devise and LinkedIn authentication February 25, 2018Leave a comment

In this article, I will use Ruby 2.5 and Rails 5.1.4 – the newest versions of both tools at the moment of writing this article. Let’s start with adding devise gem to our Gemfile file:

gem 'devise'

Now we have to run bundle install in order to install gem on our machine. However, installation is not yet finished. We have to generate devise initializer file and translation file by running following command from our command line:

rails generate devise:install

Model generation

We can now generate our model. We will name it User as it’s a common name for model with authorization data.

rails generate devise user

Devise will generate for us model class, migration and blank tests. Our User model has following contents:

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
end

Since we want our application to allow only authorization with LinkedIn we would remove not needed code:

class User < ApplicationRecord
  devise :trackable, :omniauthable
end

Our migration should have following contents:

# frozen_string_literal: true

class DeviseCreateRecruiters < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t|
      t.string :email,              null: false, default: ""

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.inet     :current_sign_in_ip
      t.inet     :last_sign_in_ip
      
      # LinkedIn
      t.string :provider
      t.string :uid, unique: true

      t.timestamps null: false
    end

    add_index :users, :email,                unique: true
  end
end

We don’t want to allow the user to authenticate using his email and password but we still want to keep user email to use it later in our application. To create a new table in our database we have to run migration:

bundle exec rake db:migrate

If you noticed that you left some columns that you don’t need, you can rollback migration and correct your file and then run migration once again:

bundle exec rake db:rollback STEP=1

Columns provider and uid will be used for LinkedIn authentication with OmniAuth strategy.

LinkedIn configuration

We would use omniauth-linkedin gem for our authorization so let’s install it first. Add new line to your Gemfile and then run bundle install:

gem 'omniauth-linkedin'

You would need now a consumer_key and consumer_secret keys for your app. If you don’t have an app yet you can check this article and create one.

Open and edit Devise gem initializer located in config/initializers/devise.rb and put your credentials:

config.omniauth :linkedin, "consumer_key", "consumer_secret"

We can now update our User model and let it know that we want to use LinkedIn as authorization provider:

class User < ApplicationRecord
  devise :trackable, :omniauthable, omniauth_providers: %i[linkedin]
end

Routing

If we have devise_for :users entry in our config/routes.rb file then Devise will automatically add two paths:

  • user_omniauth_authorize_path(provider)
  • user_omniauth_callback_path(provider)

We can now add a link to start LinkedIn authorization:

<%= link_to "Sign in with LinkedIn", user_linkedin_omniauth_authorize_path %>

Clicking on this link will redirect you to the LinkedIn authorization page. We have to prepare now our controller to receive a request and authorize given user on our application side.

Authentication controller

To handle authorization action we would create separated controller. We will name it AuthorizationsController. In order to process LinkedIn request we have to add two methods there: linkedin and failure. The first method is responsible for handling success response and the second for handling failure.

class AuthorizationsController < Devise::OmniauthCallbacksController
  def linkedin
  end

  def failure
    redirect_to root_path
  end
end

to make it work we also have to update our routes file and tell Devise which controller we want to use for LinkedIn authorization:

devise_for :users, controllers: { omniauth_callbacks: 'authorizations' }

Let’s implement now code responsible for creating and finding users:

def self.from_omniauth(auth)
  where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
    user.email = auth.info.email
  end
end

so we can update our controller and create fully working authorization:

class AuthorizationsController < Devise::OmniauthCallbacksController
  def linkedin
    @user = User.from_omniauth(request.env["omniauth.auth"])

    sign_in_and_redirect @user, event: :authentication
  end

  def failure
    redirect_to root_path
  end
end

and that’s it! We created very simple and basic application that allows user to authorize via LinkedIn network. We save only e-mail. You may want to collect other data such as name or image. Is up to you, the possibilities are endless.

When user is authorized you can access its via current_user variable in controller and views.

Download free RSpec & TDD ebook

Do you want to earn more or jump to the next level in your company? Do you know that testing skills are one of the most desired skills? There is only first step: start testing and do it right. My ebook can help you. Subscribe to the newsletter to get a free copy of the book.

Leave a Reply

Your email address will not be published. Required fields are marked *