A Talk to Remember

  • Random
  • Archive
  • RSS
  • Let's hear it

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.

    • #ruby
    • #devise
    • #rails
  • 1 year ago
  • 32
  • Comments
  • Permalink
  • Share
    Tweet

32 Notes/ Hide

  1. diana2345d likes this
  2. robertfall posted this

Recent comments

Blog comments powered by Disqus
← Previous • Next →

A Talk to Remember

About

Avatar The coming's and goings of a young tech hacker

Me, Elsewhere

  • @robertfall on Twitter
  • My Skype Info
  • robertfall on github

Twitter

loading tweets…

  • RSS
  • Random
  • Archive
  • Let's hear it
  • Mobile

Effector Theme by Carlo Franco.

Powered by Tumblr