I have followed the guide How To: Override confirmations so users can pick their own passwords as part of confirmation activation. I all works smooth.
My mailer ends up sending a confirmation link a la:
"https://example.com/users/confirmation?confirmation_token=foo"
After the user has chosen his new password ensures that the user is redirected to the standard after_sign_in_path_for:
def do_confirm
#confirmable.confirm
set_flash_message :notice, :confirmed
sign_in_and_redirect(resource_name, #confirmable)
end
But what if I want to redirect the user to an individual page, specifically tailored for this user? Like e.g.:
"https://example.com/users/confirmation?confirmation_token=foo&redirect_to=#{CGI.parse('custom_path')}"
How would I set this up?
As Tom says in the above comment, you want to override the after_sign_in_path_for method.
There is a page on devise's GitHub wiki that explains this in more detail and gives a code example (if you use OAuth it will look a little different):
class ApplicationController < ActionController::Base
protect_from_forgery
protected
def after_sign_in_path_for(resource)
sign_in_url = new_user_session_url
if request.referer == sign_in_url
super
else
stored_location_for(resource) || request.referer || root_path
end
end
end
Related
I'm using devise for users but I have two types of users (customers and suppliers), and need different redirect routes based on which path they follow. EG: If a customer signs (/signup) up it will redirect them to their dashboard. If a supplier signs up(/suppliers/registrations/user), it needs to direct them to the next form where they start describing their business(/suppliers/registrations/business). How do you manage this?
UPDATE
I've update my devise registrations controller to include the following (I've excluded all of the commented out stuff)
users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
if resource.supplier == true
redirect_to supplier_business_path
elsif resource.supplier == false
redirect_to user_projects_path(current_user)
end
end
end
But it keeps taking me to the root regardless.
after_sign_in_path_for should just return the path but not perform a redirect, try changing your method like this:
protected
def after_sign_up_path_for(resource)
if resource.supplier == true
# also add specific url = '/suppliers/registrations/user'
supplier_business_path
elsif resource.supplier == false
user_projects_path(current_user)
end
end
also refer this link:
https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-on-successful-sign-up-(registration)
Use devise after_sign_in_path_for method in aplication controller. For eg
def after_sign_in_path_for(resource)
if resource.role == "customer"
redirect_to customer_dashboard_path
elsif resource.role == "supplier"
redirect_to supplier_dashboard_path
end
end
I am on Rails 3 and the latest version of Devise, and I have a before filter in my AdminController to authenticate_user! I need to store a session variable for the request.referrer before it redirects so that I can send it back to the /admin page when they try to go on it. Where would I overwrite authenticate_user!?
What I want to do is this, but I don't know where to define it:
def authenticate_user!
session[:return_to] = request.request_uri
super
end
You don't actually need to do that, devise will respect an after_sign_in_path for this exact purpose.
In your application controller:
before_filter :set_return_path
def after_sign_in_path_for(resource)
session["user_return_to"] || root_url
end
def set_return_path
unless devise_controller? || request.xhr? || !request.get?
session["user_return_to"] = request.url
end
end
From the devise helper:
# The default url to be used after signing in. This is used by all Devise
# controllers and you can overwrite it in your ApplicationController to
# provide a custom hook for a custom resource.
# def after_sign_in_path_for(resource_or_scope)
An alternative to Matt's answer, which doesn't require the return page to be recorded upon every page view:
In application_controller.rb:
# Only remembers the page immediately before we
# redirect them for auth, instead of every page view
def authenticate_user!
session["user_return_to"] = request.fullpath
super
end
# Deletes the return path as soon as it's used, so
# they aren't accidentally redirected back again
# next time they login
def after_sign_in_path_for(resource)
session.delete("user_return_to") || root_path
end
I am using devise and created a User field called :active which is either true or false. I have to manually make the user active (true) before the user is allowed to log in. At least that is the intent. I have tried this...
class SessionsController < Devise::SessionsController
# POST /resource/sign_in
def create
"resource/signin CREATE"
self.resource = warden.authenticate!(auth_options)
unless resource.active?
sign_out
redirect_to :sorry_not_active_url
return
end
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => after_sign_in_path_for(resource)
end
end
However this does not catch all the places where a user can log in, for example, when a user changes their password, the site automatically logs them in automatically after. However, if the user is not active, I do not want them to be allowed to log in, but rather be redirected to a sorry_not_active_url.
What would be the best way to prevent the user from signing in if the user is not active?
Thank you.
Add these two methods to your user model, devise should pick them up automatically - you should NOT need to extend Devise::SessionsController
def active_for_authentication?
super && self.your_method_for_checking_active # i.e. super && self.is_active
end
def inactive_message
"Sorry, this account has been deactivated."
end
Devise (If you have devise 3.2+) now support block parameter in (session) create
# assuming this is your session controller
class SessionsController < Devise::SessionsController
def create
super do |resource|
unless resource.active?
sign_out
# you can set flash message as well.
redirect_to :sorry_not_active_url
return
end
end
end
With the Confirmable module enabled, Devise will not allow an unconfirmed user to sign in after a predefined period of time has elapsed. Instead the user is redirected back to the sign in page with the flash message "You have to confirm your account before continuing".
This is an undesirable interaction model, as a flash notice does not provide adequate space to properly explain to the user why access has been denied, what "confirm your account" means, provide a link to resend the confirmation, and instructions on how to check your spam folder and so on.
Is there a way I can change this behaviour to redirect to a specific URL instead?
Sorry at first I thought you meant after Sign Up not Sign In. So the down below works for how to direct users after Sign Up and what you need to do for Sign In is to create a custom Devise::FailureApp
See the wiki page: https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-when-the-user-can-not-be-authenticated
Then within your custom FailureApp overwrite redirect_url method from https://github.com/plataformatec/devise/blob/master/lib/devise/failure_app.rb:
def redirect_url
if warden_message == :unconfirmed
custom_redirect_path
else
super
end
end
For custom redirect after Sign Up:
There is a controller method after_inactive_sign_up_path_for within the RegistrationsController that you can overwrite to accomplish this.
First in your Routes you will need to specify to use your custom controller:
config/routes.rb:
devise_for :users, :controllers => { :registrations => "users/registrations" }
Second you create your custom controller that inherits from the normal controller in order to overwrite the method:
app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
protected
def after_inactive_sign_up_path_for(resource)
signed_up_path
end
end
In this case for my App my Devise model is User so you may want to change that namespace if your model is named differently. I wanted my users to be redirected to the signed_up_path, but you can change that to your desired path.
I just did this, but took a different approach.
in app/controllers/sessions_controller.rb:
class SessionsController < Devise::SessionsController
before_filter :check_user_confirmation, only: :create
#
# other code here not relevant to the example
#
private
def check_user_confirmation
user = User.find_by_email(params[:email])
redirect_to new_confirmation_path(:user) unless user && user.confirmed?
end
end
This worked for me and seemed minimally invasive. In my app new sessions always have to go through sessions#create and users always sign in with their email address, so this may be a simpler case than yours.
You can of course redirect_to any location you desire in the check_user_confirmation method. new_confirmation_path was the logical choice for me because it provides users with the resources to get confirmed.
This is my solution you need to add :unconfirmed message on devise locales below the sessions.
in app/controllers/sessions_controller.rb
def check_user_confirmation
user = User.where(email: params[:user][:email]).take
unless user && user.confirmed?
set_flash_message! :alert, :unconfirmed
expire_data_after_sign_in!
respond_with user, location: after_inactive_sign_up_path_for(user)
end
end
protected
def after_inactive_sign_up_path_for(resource)
new_user_session_path
end
I am trying to figure out how to redirect a user to the page they logged in from (or failed to login) using Warden/Devise. I figure there is a session variable somewhere that is either available or could be made available.
E.g.
Scenario 1:
Non-authorized user goes to protected page X;
Redirected to login page;
User logs in;
User redirected to protected page x
Scenario 2:
Non-authorized user wants to take a protected action on page x;
User clicks on login link;
User logs in;
User redirected to page x where the action is now available
Any pointers are appreciated.
Thanks!
There is a devise helper method called after_sign_in_path_for(resource) (http://rdoc.info/github/plataformatec/devise/master/Devise/Controllers/Helpers) , and a session variable called session[:"user.return_to"] which stores the last url. The after_sign_in_path_for method needs to return a string, then devise automatically uses this path to redirect the user after login.
In my application controller I have put the following which redirects my users to the home page if the session variable is not set:
def after_sign_in_path_for(resource)
(session[:"user.return_to"].nil?) ? "/" : session[:"user.return_to"].to_s
end
If your using CanCan for authorization you can accomplish this be adding the following. If not, you should be able to adapt the concepts into your current authorization system.
app/controllers/application_controller.rb
rescue_from CanCan::AccessDenied do |exception|
flash[:error] = exception.message
if user_signed_in?
redirect_to root_url
else
# Adds the protected page to the login url but only if the user is not logged in
redirect_to login_path(:next => request.path)
end
end
def after_sign_in_path_for(resource_or_scope)
# if a protected page found, then override the devise after login path
params[:user]["next"] || super
end
app/views/devise/sessions/new.html.erb
<% if params[:next] %>
<%= f.hidden_field :next, :value => params[:next] %>
<% end %>
Instead of using session variables this solution uses params in the URL to keep track of the protected page.
Wow, just realized that devise (3.5.2) does this all by itself behind the scenes (around Devise::SessionsController#new action), no additional controller modifications required.
If you need to explicitly store/get previous location, please see my previous answer:
Currently (Fall 2015) there's a sexier way to do that:
Devise::Controllers::StoreLocation#store_location_for:
# Stores the provided location to redirect the user after signing in.
# Useful in combination with the `stored_location_for` helper.
store_location_for :user, dashboard_path
redirect_to user_omniauth_authorize_path :facebook
Devise::Controllers::StoreLocation#stored_location_for:
# Returns and delete (if it's navigational format) the url stored in the session for
# the given scope. Useful for giving redirect backs after sign up:
redirect_to stored_location_for(:user) || root_path
The methods handle related session key and value deletion after reading, all you need is to provide your :resource key (:user in the example above) and a path to store (dashboard_path in the example above). See source for the details.
As for the actual answer it'll be something like that:
class ApplicationController < ActionController::Base
rescue_from CanCan::AccessDenied, with: :access_denied
# ...
private
def access_denied(exception)
store_location_for :user, request.path
redirect_to user_signed_in? ? root_path : new_user_session_path, alert: exception.message
end
def after_sign_in_path_for(resource)
stored_location_for(:user) || root_path
end
end
You can use request.referer to get the previous URL.
here's the best I could come up with. Works perfectly also with facebook authentication. by adding more restrictions to the prepending of urls to the session variable you can remove more and more paths you don't want the user to return too (e.g. callbacks, splash pages, landing pages, etc)
#ApplicationsController
after_filter :store_location
def store_location
session[:previous_urls] ||= []
# store unique urls only
session[:previous_urls].prepend request.fullpath if session[:previous_urls].first != request.fullpath && request.fullpath != "/user" && request.fullpath != "/user/login" && request.fullpath != "/" && request.fullpath != "/user/logout" && request.fullpath != "/user/join" && request.fullpath != "/user/auth/facebook/callback"
# For Rails < 3.2
# session[:previous_urls].unshift request.fullpath if session[:previous_urls].first != request.fullpath
session[:previous_urls].pop if session[:previous_urls].count > 3
end
def after_sign_in_path_for(resource)
#url = session[:previous_urls].reverse.first
if #url != nil
"http://www.google.com" + #url
else
root_path
end
end