Token Authentication for Rail 5.1 API app using Devise

Add the gem:

gem 'simple_token_authentication'

and run bundle. Generate and run the migration.

rails g migration add_authentication_token_to_users "authentication_token:string{30}:uniq"
rails db:migrate

Add acts_as_token_authenticatable in user model. The migration will generate this file:

class AddAuthenticationTokenToUsers < ActiveRecord::Migration[5.1]
  def change
    add_column :users, :authentication_token, :string, limit: 30
    add_index :users, :authentication_token, unique: true
  end
end

It creates the column and defines the index. Define the token reset method in user model.

def reset_authentication_token!
  authentication_token = nil
  save # automatically generates a new authentication token
  authentication_token 
end

The save does not automatically generate new authentication token as advertised. So I had to add the following method to user.rb:

  def reset_authentication_token!
    update_column(:authentication_token, Devise.friendly_token)
  end

We can now implement the logout feature by implementing the destroy action in sessions controller:

  def destroy
    current_user.reset_authentication_token!
    head :ok
  end

  private

  def current_user
    authenticate_with_http_token do |token, options|
      User.find_by(authentication_token: token)
    end
  end    

Add acts_as_token_authentication_handler_for User to the sessions controller. Login using curl:

curl -X POST --data "email=bugs@rubyplus.com&password=welcome" http://localhost:3000/sessions

This gives the error:

NoMethodError (undefined method `authenticate_user!' for 

Inside devise.rb change:

config.navigational_formats = ['*/*', :html]

to:

config.navigational_formats = [:json]

This gives an error:

Error: You need to sign in or sign up before continuing rails devise

Resolution:

acts_as_token_authentication_handler_for User, fallback_to_devise: false

Final sessions controller:

class SessionsController < ApplicationController
  acts_as_token_authentication_handler_for User, fallback_to_devise: false

  def create
    success, user = User.valid_login?(params[:email], params[:password])
    if success
      render json: user.as_json(only: [:email, :authentication_token]), status: :created  
    else
      head :unauthorized
    end
  end

  def destroy
    current_user.reset_authentication_token!
    head :ok
  end

  private

  def current_user
    authenticate_with_http_token do |token, options|
      User.find_by(authentication_token: token)
    end
  end    
end  

Logout:

curl -X DELETE -H "Authorization: Token token=p2obxpqW65RstFsQcmcn" http://localhost:3000/sessions/1

Use the token that was provided in the successful login response. This gave an error:

NoMethodError (undefined method `authenticate_with_http_token' for

Cleanup routes.rb:

post 'sessions' =>  'sessions#create'
delete 'sessions' => 'sessions#destroy'

You can now logout without giving any fake parameter in the URL:

curl -X DELETE -H "Authorization: Token token=p2obxpqW65RstFsQcmcn" http://localhost:3000/sessions

If you get:

NoMethodError: undefined method `devise' for User

Add extend Devise::Models to user model.

In this article, we saw how to use simple_token_authentication gem to authenticate a client using Devise.


Related Articles


Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.