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
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