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