Search code examples
ruby-on-railsdeviseapartment-gem

Rails Sign in using devise to authenticate from two db tables


I have 2 database tables namely coreteam and user. Both the models are configured with devise. For now to sign in for core team my API is: localhost:3000/api/v1/core_teams/sign_in with following request data.

{ 
  "core_team": {
    "email": "[email protected]",
    "password": "Password21"
  }
}

And the API for users to sign in is: localhost:3000//api/v1/users/sign_in with the following request data.

{
  "user": {
    "email": "[email protected]",
    "password": "Password20"
  }
}

And following is my routes.rb file

 Rails.application.routes.draw do

 root to: 'users#index'

scope 'api/v1', defaults: { format: :json } do
 devise_for :core_teams, controllers: {
   sessions: 'api/v1/sessions',
   passwords: 'api/v1/passwords'
  }

 devise_for :users, controllers: {
   sessions: 'api/v1/sessions',
   invitations: 'api/v1/invitations',
   passwords: 'api/v1/passwords',
   registrations: 'api/v1/registrations'
  }
  end
end

And I am using devise sessionscontroller to login. Only difference is the way I am rendering the data.

My sessions_controller.rb is as follows:

module Api
 module V1
  class SessionsController < Devise::SessionsController
   private

    def respond_with(resource, _opts = {})
      render json: resource
    end

    def respond_to_on_destroy
      head :no_content
    end

   end
  end
 end

Now my aim is to have only one API call to sign in using the same type of requests data. Reason to have two different user table is my app is kind of multi-tenanted and core team is outside of tenant and users are inside tenant.

CoreTeam and Tenant Architecture


Solution

  • Try this

    routes

    post "/api/v1/sign_in" => "api/v1/sessions#create"
    
    devise_for :users, ...
    devise_for :core_teams, ...
    

    api/v1/sessions_controller.rb

    module Api
      module V1
        class SessionsController < Devise::SessionsController
          before_action :rewrite_request_params, only: %i[create]
    
        protected
          def devise_mapping
            @devise_mapping ||= Devise.mappings[account_name || :user]
          end
    
        private
          def rewrite_request_params
            return unless account_name
            request.params[account_name] = {
              email: params[:email],
              password: params[:password],
            }
          end
    
          def account_name
            @account_name ||= account.class.name.underscore.to_sym if account
          end
    
          def account
            return if params[:email].blank?
            return @account if defined? @account
            @account = CoreTeam.find_by(email: params[:email]) || User.find_by(email: params[:email])
          end
        end
      end
    end
    

    this will allow you to submit params to /api/v1/sign_in for both User and CoreTeam

    First it will look for an account in core_teams table then users table. if found it will rewrite the request.params

    # core_teams
    $.post('/api/v1/sign_in', {
      "email": "[email protected]",
      "password": "Password21"
    })
    
    # users
    $.post('/api/v1/sign_in', {
      "email": "[email protected]",
      "password": "Password20"
    })