How To: Redirect back to current page after sign in, sign out, sign up, update

INOUE Takuya edited this page Jun 18, 2018 · 51 revisions

This guide reflects current versions of Devise (written as of 4.3 tested on 4.2).

Redirecting back to the "current page" involves saving the current url in the session and then retrieving the url from the session after the user is authenticated / signed out. This should only really be done for GET requests as the other http methods (POST, PUT, PATCH, DELETE) are not idempotent and should not be repeated automatically.

Why not use request.referer?

request.referer is set by the value of the HTTP referer header. Many browsers do not send this header. Therefore the only robust cross-browser way to implement this functionality is by using the session.

StoreLocation to the rescue

The Devise::Controllers::StoreLocation helper module defines the store_location_for and stored_location_for helpers which store and retrieve the values from the session - and which are used internally by Devise.

To store the location for your whole application use before_action to set a callback (Use before_filter in Rails versions before 4.0).

# This example assumes that you have setup devise to authenticate a class named User.
class ApplicationController < ActionController::Base
  before_action :store_user_location!, if: :storable_location?
  # The callback which stores the current location must be added before you authenticate the user 
  # as `authenticate_user!` (or whatever your resource is) will halt the filter chain and redirect 
  # before the location can be stored.
  before_action :authenticate_user!

  private
    # Its important that the location is NOT stored if:
    # - The request method is not GET (non idempotent)
    # - The request is handled by a Devise controller such as Devise::SessionsController as that could cause an 
    #    infinite redirect loop.
    # - The request is an Ajax request as this can lead to very unexpected behaviour.
    def storable_location?
      request.get? && is_navigational_format? && !devise_controller? && !request.xhr? 
    end

    def store_user_location!
      # :user is the scope we are authenticating
      store_location_for(:user, request.fullpath)
    end
end

To redirect to the stored location after the user signs in you would override the after_sign_in_path_for method:

def after_sign_in_path_for(resource_or_scope)
  stored_location_for(resource_or_scope) || super
end

Legacy versions of Devise prior to 3.2.1

You can provide your own storage mechanism or backport StoreLocation (upgrading is recommended though):

module StoreLocationBackport
  def store_location_for(resource_or_scope, location)
    session_key = stored_location_key_for(resource_or_scope)
    session[session_key] = location
  end
  
  def stored_location_for(resource_or_scope)
    session_key = stored_location_key_for(resource_or_scope)
    if is_navigational_format?
      session.delete(session_key)
    else
      session[session_key]
    end
  end

  def after_sign_in_path_for(resource_or_scope)
    stored_location_for(resource_or_scope) || super
  end
  
  private 
    def stored_location_key_for(resource_or_scope)
      scope = Devise::Mapping.find_scope!(resource_or_scope)
      "#{scope}_return_to"
    end
end

# This example assumes that you have setup devise to authenticate a class named User.
class ApplicationController < ActionController::Base
  # @todo Update Devise!
  # Remove this after updating Devise 
  include StoreLocationBackport

  before_action :store_user_location!, if: :storable_location?
  # The callback which stores the current location must be added before you authenticate the user 
  # as `authenticate_user!` (or whatever your resource is) will halt the filter chain and redirect 
  # before the location can be stored.
  before_action :authenticate_user!

  private 
    # Its important that the location is NOT stored if:
    # - The request method is not GET (non idempotent)
    # - The request is handled by a Devise controller such as Devise::SessionsController as that could cause an 
    #    infinite redirect loop.
    # - The request is an Ajax request as this can lead to very unexpected behaviour.
    def storable_location?
      request.get? && !devise_controller? && !request.xhr? 
    end

    def store_user_location
      # :user is the scope we are authenticating
      store_location_for(:user, request.fullpath)
    end
end
Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.