Question

I'm trying to add another field in the sign in page using Devise and Rails 4.0. A user needs to provide their username, password and an organization/location code. A user has_and belongs_to_many locations, and when logged in, this organization code should be saved in sessions.

At this point, I think I got most of it working (please let me know if there is a better way of doing this), but I don't know how to handle what happens if an invalid location code is entered. Here's what I have so far.

class SessionsController < Devise::SessionsController
  def create
    user = User.find_by_username(params["user"]["username"])
    org_code = params["user"]["check_org_code"].downcase
    user.locations.each do |location|
      if location.org_code == org_code
        super
        session[:location] = location
      else
        < return a warden.authenticate! fail? >
        super
      end
    end
  end
end
Was it helpful?

Solution

I initially solved this by using throw(:warden) and while this works, is probably not the best solution. Later I came across a custom devise strategy example by r00k and implemented my own variation. I've included both solutions below.

r00k-esque solution N.B. there were some bits of code is r00k's solution that didn't work for me. In case r00k's solution isn't working for you, please note the differences with "fail!" and "valid_password?".

# config/initializers/local_override.rb

require 'devise/strategies/authenticatable'

module Devise
  module Strategies
    class LocalOverride < Authenticatable
      def valid?
        true
      end

      def authenticate!
        if params[:user]
          user = User.find_by_username(params["user"]["username"])
          if user.present?
            code = params["user"]["check_org_code"].downcase
            codes = user.locations.pluck(:org_code).map{ |code| code.downcase }
            check_code = code.in?(codes)
            check_password = user.valid_password?(params["user"]["password"])

            if check_code && check_password
              success!(user)
            else
              fail!(:invalid)
            end
          else
            fail!(:invalid)
          end
        else
          fail!(:invalid)
        end
      end
    end
  end
end

Warden::Strategies.add(:local_override, Devise::Strategies::LocalOverride)

# config/initializers/devise.rb
config.warden do |manager|
  manager.default_strategies(:scope => :user).unshift :local_override
end

Initial solution

class SessionsController < Devise::SessionsController
  def create
    user = User.find_by_username(params["user"]["username"])
    org_code = params["user"]["check_org_code"].downcase
    matching = false

    user.locations.each do |location|
      if location.org_code == org_code
        super
        session[:location] = location
        matching = true
        break
      end
    end

    if matching == false
      throw(:warden, :message => :invalid)
    end
  end
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top