简体   繁体   中英

Redirect loop with Devise after_sign_in_path_for

I'm having a bit of a noob issue. I wanted to get devise to redirect to the last page the user visited. So I did the following...

def after_sign_in_path_for(resource)
    request.referer
end

Works great...except if the user is actually logging in through the original form which causes a redirect loop.

I tried

def after_sign_in_path_for(resource)
   if (request.referer == "/users/sign_in")
  :pages_home
 else
  request.referer
 end

end

But thats not working, most likely because I have no idea what request.referer is actually returning when it encounters the original user login page (www.example.com/users/sign_in).

Any ideas?

tldr; Using devise, I want to redirect to the page logged in from (ie /blog/4) unless the page is /users/sign_in

SOLVED:

Matchu was right. The request.referer was returning the domain as well...

http://example.com/users/sign_in

(note: no www prefix)

I'm still interested in an alternative to request.referer if its an insecure or inefficient way.

Don't redirect to referrers - it's generally a bad idea.

Instead, pass a next value across in the query-string or form-data. Perhaps use something like:

def after_sign_in_path_for(resource)
  params[:next] || super
end

When a user tries to visit a page requiring authentication (eg, /admin/posts/3/edit ) the authentication before_filter issues a redirect_to new_session_url(:next => request.path) . Then code up the login action and view to preserve the :next query-string parameter.

How about this:

def after_sign_in_path_for(resource)
  sign_in_url = url_for(:action => 'sign_in', :controller => 'users', :only_path => false, :protocol => 'http')  
  if (request.referer == sign_in_url)
    super
  else
    request.referer
  end
end

I took Justice's answer and changed it to use sessions instead.

As far as I can see, sessions are simpler then adding the url as param, but they might behave unexpected when a user is browsing a site in multiple tabs, for example. Using sessions is less RESTfull, but simpler and cleaner.

When using CanCan, setting the redirect-path can be done in a central place: the place that handles the "access denied" exceptions:

  rescue_from CanCan::AccessDenied do |exception|
    if current_user.nil?
      session[:next] = request.fullpath
      puts session[:next]
      redirect_to new_user_session_path, :alert => exception.message
    else
      render :file => "#{Rails.root}/public/403.html", :status => 403
    end
  end

But you can set this anywhere, actually:

 def edit
   if current_user.roles[:moderator].nil?
     session[:next] = "/contact"
     redirect_to new_user_session_path, :alert => "please contact the moderator for access"
   end
   # ...
 end

Then, in ApplicationController , you can re-use that session value. Make sure to remove it too, though.

  def after_sign_in_path_for(resource)
    path = ''
    if session[:next]
      path = session[:next]
      session[:next] = nil
    else
      path = super
    end

    path
  end

Here is my code to rescue_from CanCan.

rescue_from CanCan::AccessDenied do |exception|
  if current_user.nil?
    session[:next] = request.fullpath
    puts session[:next]
    redirect_to login_url, :alert => "You have  to be logged in to continue"
  else
    #render :file => "#{Rails.root}/public/403.html", :status => 403
    if request.env["HTTP_REFERER"].present?
      redirect_to :back, :alert => exception.message
    else
      redirect_to root_url, :alert => exception.message
    end
  end
end

What it does: - if the user is not logged in, it redirects to login page - if the user is logged in, but do not have abilities to see the action, it redirects either back or to the root page with alert flash message

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM