Devise Gotcha
So for my Unisa project I’m using Devise on Rails for my authentication. Tonight I ran into a very tricky situation.
Devise automatically handles the creation and activation of users for an application. This is great until you start heading off the beaten track. First, my “users” aren’t called users because they are associated with different profiles, I stupidly decided to call the Identities. Devise copes well with this because you can specify the class name when generating the Devise install, but be aware, this changes MANY of the helper names, for instance: authenticate_user! becomes authenticate_identity!.
That’s ok, I figured that out quite easily, but what happened next frustrated me for a couple of hours.
I want my Identities to be able to see a summary of themselves when going to localhost:3000/account. Obviously to do this they need to be logged in, and the obvious place to put the action would be in the IdentitiesController. Hold on, there isn’t one, devise does the magic! No problem, you can add methods by inheriting from the Devise::SessionsController class and adding a couple of routes, as follows:
devise_for :identities
devise_scope :identity do
get 'account' => 'identities#show', :as => 'landing_page'
end
This allows you to start creating actions in the IdentitiesController class as usual. So I tried this:
class IdentitiesController < Devise::SessionsController
before_filter :authenticate_identity!, :only => :show
def show
@identity = current_identity
end
end
You’ve probably guessed though that this didn’t work. Authentication was just not happening because this was a Devise controller. So after long hours of hunting I found this snippet in this file:
def self.define_helpers(mapping) #:nodoc:
mapping = mapping.name
class_eval <<-METHODS, __FILE__, __LINE__ + 1
def authenticate_#{mapping}!(force = false)
warden.authenticate!(:scope => :#{mapping}) if !devise_controller? || force
end
def #{mapping}_signed_in?
!!current_#{mapping}
end
def current_#{mapping}
@current_#{mapping} ||= warden.authenticate(:scope => :#{mapping})
end
def #{mapping}_session
current_#{mapping} && warden.session(:#{mapping})
end
METHODS
ActiveSupport.on_load(:action_controller) do
helper_method "current_#{mapping}", "#{mapping}_signed_in?", "#{mapping}_session"
end
end
The culprit here is the first method definition in the string. Devise has these definitions in a string so it can interpolate my “Identity” scope into the method names. It made it almost impossible to find however. What the first method essentially does is ignore authentication for devise controllers:
def authenticate_#{mapping}!(force = false)
warden.authenticate!(:scope => :#{mapping}) if !devise_controller? || force
end
Great, all I have to do is pass force through to the authenticate call for it to work. But how do you pass parameters through to a before_filter? Here’s how:
before_filter :only => :show do |controller|
controller.authenticate_identity! true
end
Finally it was working as expected. I hope this helps someone with the same problem.
32 Notes/ Hide
-
diana2345d likes this
-
robertfall posted this