Sunday, 15 January 2017

User authentication Api with Devise.


In this blog we will create sign-up and login Api with devise. For this we will override registration controller and session controller of devise. For this you can follow devise documentation.

So, in Gemfile -

gem 'devise'

Then run bundle install

As we know that we need to run the devise generator

$ rails generate devise:install

Now, configure the default Devise modules with User model

$ rails generate devise User

For overriding devise's controller, create your custom controllers using the generator

$ rails generate devise:controllers User

We have to generate auth_token for every user for authentication

So, in User.rb

class User < ApplicationRecord

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


  #==== Callbacks =======================================================
  before_create :generate_auth_token


  def generate_token
    SecureRandom.urlsafe_base64
  end


  private

  def generate_auth_token
    if self.auth_token.blank?
      begin
        self.auth_token = generate_token
      end while User.exists?(auth_token: self.auth_token)
    end
  end

end

We have to override routes also.

In roures.rb

Rails.application.routes.draw do
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
  devise_for :users, controllers: {
                       registrations: 'users/registrations',
                       sessions: 'users/sessions'
                   }

  devise_scope :user do
    root to: "users/sessions#new"
    post '/sign-up' => 'users/registrations#create'
    post '/sign-in' => 'users/sessions#create'
  end
end


Now, we will override registration controller -

So, in users/registrations_controller.rb-

class Users::RegistrationsController < Devise::RegistrationsController

=begin
********************************************************************
  POST /resource.json
  params -
      {
       "user":{
          "first_name":"xyz",
          "last_name":"abc",
          "phone_number":"0987654321",
          "email":"xyz@xyz.com",
          "password":"12345678",
          "password_confirmation":"12345678"
       }
     }
********************************************************************
=end
  def create
    resource = User.new(configure_sign_up_params)
    respond_to do |format|
      if resource.save
        format.json { render json: {resource: resource}, status: :created }
      else
        format.json { render json: {error: resource.errors.full_messages}, status: :unprocessable_entity }
      end
    end
  end


  protected

  # If you have extra params to permit, append them to the sanitizer.
  def configure_sign_up_params
    params.require(:user).permit(:email, :first_name, :last_name, :phone_number, :password, :password_confirmation)
  end

end

Above code will return user obejct as json -


{
  "resource": {
    "auth_token": "NywmeVFFU1eyZE_BGBA4og",
    "id": 1,
    "first_name": "xyz",
    "last_name": "abc",
    "phone_number": "0987654321",
    "created_at": "2017-01-12T09:57:18.000Z",
    "updated_at": "2017-01-12T10:03:00.000Z",
    "email": "xyz@xyz.com"
  }
}

For login we have to override sessions_controller.rb

class Users::SessionsController < Devise::SessionsController

  #====== Filters ===============================================
  prepend_before_action :require_no_authentication, :only => [:create]
  skip_before_action :verify_signed_out_user, if: -> { request.format.json? }

=begin
*********************************************************
  POST /sign-in.json
  params - {"user":{"email": "xyz132@xyz.com", "password": "12345678"}}
*********************************************************
=end
  def create
    respond_to do |format|
      resource = User.find_for_database_authentication(email: params[:user][:email])
      if resource and resource.valid_password?(params[:user][:password])
        resource.auth_token = resource.generate_token
        format.json { render json: {resource: resource}, status: :ok }
      else
        format.json { render json: {error: 'Sign in not successful'}, status: :unprocessable_entity }
      end
    end
  end

end

On successful it will also return user object as josn -

{
  "resource": {
    "auth_token": "NywmeVFFU1eyZE_BGBA4og",
    "id": 1,
    "first_name": "xyz",
    "last_name": "abc",
    "phone_number": "0987654321",
    "created_at": "2017-01-12T09:57:18.000Z",
    "updated_at": "2017-01-12T10:03:00.000Z",
    "email": "xyz@xyz.com"
  }
}


Note that In api authentication we should not generate session for user because it should handle at front-end.

If you have web app also than you have to create session for user and use respond with html too. 


1 comment:

  1. Excellent article. Very interesting to read. I really love to read such a nice article. Thanks! keep rocking.Ruby on Rails Online Course

    ReplyDelete