Where to override current_user helper method of devise gem
Asked Answered
R

4

31

How can i override current_user of devise gem. Actually I need to add web services for mobile-app.

Currently devise is managing session and 'current_user' for web-application.

Now Mobile app will send user_id to the server. I need to override current user like this

def current_user
  if params[:user_id].blank?
    current_user
  else
    User.find(params[:user_id])
  end   
end

Should I need to modify devise gem as plugin ? or something else ?
Kindly explain in detail as I am new in rails.

Kind regards,

Resplendent answered 27/4, 2012 at 11:14 Comment(3)
yikes... so, are you saying when a mobile agent sends up a user_id you'll just trust it?Guild
also some authentication secret keyResplendent
Devise will handle that token_authenticatable - just passing authentication_token up, no need for user_id.Guild
O
60

According to the module Devise::Controllers::Helpers, current_user (together with all other devise helpers) is added to ApplicationController, which means that you can override it in this way:

# in application_controller.rb

def devise_current_user
  @devise_current_user ||= warden.authenticate(scope: :user)
end

def current_user
  if params[:user_id].blank?
    devise_current_user
  else
    User.find(params[:user_id])
  end   
end
Orville answered 27/4, 2012 at 11:36 Comment(6)
Error on the "alias_method" row: undefined_method 'current_user' for class ApplicationController (NameError), when starting up my environment. If I comment it out, boot up, and uncomment, it works fine. Got a fix?Marra
Frexuz, try replacing the alias_method with something like: def devise_current_user { @devise_current_user ||= warden.authenticate(:scope => :user) }Cangue
is this thread-safe? I'm getting nil errors in weird points in controllers after authentication on MRI.Alltime
why alias current_user instead of calling super?Thadeus
alias_method is better than calling super in case you'd actually need to acccess the original current_user. You may need it at some point (eg. if current_user != devise_current_user #etc.).Gober
What for @devise_current_user? What is that?Gladsome
C
6

The other answer suggesting aliasing the method is actually not the best solution. Doug English's comment is the best solution:

# In ApplicationHelper
def devise_current_user
   @devise_current_user ||= warden.authenticate(:scope => :user)
end

Here's the reason:

Suppose you're including your ApplicationHelper in your controller. If you need a method in your ApplicationHelper that relies on devise_current_user, given the alias solution inside the controller, you're out of luck.

But with the explicit method definition above, you can move the definition to the helper and call it from other methods and you still get to include it in the controller.

This is useful, for example, when you're developing a user impersonation solution and you need to show two different users in the view, one for the real admin (devise_current_user) and the other, the impersonated user (current_user).

Chestonchest answered 2/9, 2014 at 22:52 Comment(1)
Indeed you are right. Please mention that the following should be included in the ApplicationController : include ApplicationHelper. Otherwise, devise_current_user becomes unknown.Gober
T
1

Limbo-Peng's answer is great, but can be improved a little to make sure only admins can do this:

You'll need to also define a is_admin? method or is_admin attribute on the User class.

You may also want to use a different key than user_id, so it will never conflict with your regular parameters.

# to impersonate another user, e.g. for customer support
# only admins can do this..
#
alias_method :devise_current_user, :current_user
def current_user
  if ! params[:user_id].blank? \
     && devise_current_user && devise_current_user.is_admin? 
    User.find(params[:user_id])
  else
    devise_current_user
  end
end
Tony answered 19/2, 2017 at 19:7 Comment(0)
G
0

Assuming we can trust our session data (which relies on whether you put user input in there without proper authorization or not), this might work as a Concern:

module Concerns
  module ControllerWithImpersonation
    extend ActiveSupport::Concern

    included do
      helper_method :devise_current_user
    end

    def current_user
      if session[:impersonated_user_id].blank?
        devise_current_user
      else
        User.find(session[:impersonated_user_id])
      end
    end

    def devise_current_user
      @devise_current_user ||= warden.authenticate(:scope => :user)
    end

  end
end

I'm using this in a project for now.

A minor question (in the answer, sorry) ... should I be aware of any changes in Devise or Warden that make devise_current_user above outdated?

Gombach answered 28/10, 2015 at 8:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.