-
Notifications
You must be signed in to change notification settings - Fork 5.5k
How To: Redirect back to current page after sign in, sign out, sign up, update
The following guides are already implemented in Devise 4.7 version
The guide below 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.
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.
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
Imagine we have the following devise user User
.
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
after_action :store_action
def store_action
return unless request.get?
if (request.path != "/users/sign_in" &&
request.path != "/users/sign_up" &&
request.path != "/users/password/new" &&
request.path != "/users/password/edit" &&
request.path != "/users/confirmation" &&
request.path != "/users/sign_out" &&
!request.xhr?) # don't store ajax calls
store_location_for(:user, request.fullpath)
end
end
end
The stored location is used by the after_sign_in_path_for helper. By setting it the helper will use it.
In this case the user will be redirected to the last viewed page before he is authenticated. You can replace request.fullpath
by anything you want.
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