Is there a way in Devise 1.0, the library for Rails 2.3, to redirect to a specific URL and not root_url after logging in?
EDIT: forgot to mention it's Devise 1.0
Chances are that your user is being redirected before after_sign_in_path is called. This happens if the user tries to go to a page that is protected by authentication directly. This will happen all the time if you have your root_path ('/') protected by authentication.
There's a discussion on google groups about this topic:
http://groups.google.com/group/plataformatec-devise/browse_thread/thread/454c7ea651601a6c/64f0adc3be8036d0?#64f0adc3be8036d0
The quick and dirty solution is to overwrite stored_location_for to always return nil like so:
class ApplicationController < ActionController::Base
...
private
def stored_location_for(resource_or_scope)
nil
end
def after_sign_in_path_for(resource_or_scope)
my_favorite_path
end
end
I think the after_sign_in_path_for method in Devise is what you're looking for.
Define that method in your ApplicationController and it will over-ride Devise's default implementation. This is what the documentation specifies to do.
Details here: http://rdoc.info/github/plataformatec/devise/master/Devise/Controllers/Helpers:after_sign_in_path_for
Suppose you want to show user's dashboard after logging in.
class HomesController < ApplicationController
def index
if current_user //returns nil if not logged in
#users = User.all
render :dashboard
end
end
def dashboard
#users = User.all
end
end
in routes.rb:
root :to => 'homes#index'
If logged in, if-block is entered in index action and dashboard.erb is rendered.
(make sure to initialize all variables, required by dashboard.erb, in your if-block)
Otherwise rails renders index.erb
Related
Spree 2.3 with spree_auth_devise, Rails 4.0
I am trying to redirect users after sign in based on their role. The best solution would be to modify a path, a la Devise, in an initializer, but these don't seem to exist for Spree. The next best solution would be to create a decorator on the sessions controller, but I can't find this controller, nor can I access it when I attempt to follow the information from rake routes
How can I redirect users to a new location after login, in a Spree app, based on their role?
UPDATE
Overwriting the after_sign_in_path_for(resource) method results in the method being triggered, but still rerouting to the admin_path.
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
byebug # this is triggered
root_path
end
end
### OR ####
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
byebug # this is triggered
if spree_current_user.has_spree_role?("admin")
admin_path
elsif spree_current_user.has_spree_role?("designer")
new_designers_spree_variant_path
else
root_path
end
end
end
My attempts are being documented here:
https://gist.github.com/asteel1981/0f258260974f4d748fb5
Thanks in advance.
Thanks to #mvidaurre for spending some time with me drilling down into this.
The issue is that spree_auth_devise has a second method that attempts to reroute to the last page attempted, meaning I needed to modify not only the after_sign_in_path_for method, but the Spree method redirect_back_or_default(default).
Additionally, because my user signs in through the admin/sign_in route, I needed to access the Spree::Admin::UserSessionsController instead of simply the Spree::UserSessionsController.
My early solution looks like this:
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
if spree_current_user.has_spree_role?("admin")
admin_path
elsif spree_current_user.has_spree_role?("designer")
'/designers/spree_variants/new' #rails helper gives the wrong path, not sure why
else
root_path
end
end
end
# app/controllers/spree/admin/user_sessions_controller.rb
Spree::Admin::UserSessionsController.class_eval do
def redirect_back_or_default(default)
if spree_current_user && spree_current_user.has_spree_role?("admin")
redirect_to(session["spree_user_return_to"] || default)
session["spree_user_return_to"] = nil
else
redirect_to(default)
end
end
end
The relevant spree source code is here:
https://github.com/spree/spree_auth_devise/blob/8cb2d325b2c1da02cbe137404d8dda89cc1613a2/lib/controllers/backend/spree/admin/user_sessions_controller.rb
Thanks for your answer in the comments. spree_auth_devise defines the Spree::UserSessionsController you can decorate the controller and use the method described in: How To: redirect to a specific page on successful sign in
You can implement your custom method for:
def after_sign_in_path_for(resource)
current_user_path
end
Maybe something like this:
def after_sign_in_path_for(resource)
if spree_current_user.has_spree_role?("designer")
root_path
elsif spree_current_user.has_spree_role?("admin")
admin_path
else
root_path
end
end
Spree documentation seems to cover this, check out:
http://guides.spreecommerce.com/developer/authentication.html
It seems to be a good starting point for what you're trying to achieve.
I have extended the devise controller for sessions. To add some extra functionality when a user logs in. Now upon login, IF no username or password are entered I get error :
SessionsController#create
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
I searched last week for up to 2 hours how to fix this anyone can help me out on this one? Would be highly appreciated
How to correctly add my custom functionality but still preserve the Devise create action?
class SessionsController < Devise::SessionsController
def create
#user = User.where(:id => current_user.id).first
#moderated = Asset.where(:attachable_id => current_user.id, :moderated => true).first
if #user.sign_in_count.to_i == 1
def after_sign_in_path_for(resource)
"/welcome/basics"
end
else
if #moderated.nil?
unless #user.has_photo?
def after_sign_in_path_for(resource)
"/home/no_photo"
end
end
else
def after_sign_in_path_for(resource)
"/home/moderated"
end
end
end
end
end
If your additional functionality consists of redirecting the user to a different page at his first login i would suggest defining after_sign_in_path_for in the application controller as suggested by the wiki.
class ApplicationController < ActionController::Base
private
def after_sign_in_path_for(resource)
if current_user.sign_in_count == 1
"/welcome/basics"
else
"/other/path"
end
end
end
Please note that this only works if User is the only resource that can sign into your application. Otherwise you would have to differentiate in this method as well via the resource parameter.
I working on an app with user authorization. It has a List and User classes. The authentication was built with Ryan Bates http://railscasts.com/episodes/270-authentication-in-rails-3-1
I'm not sure about authorization process. I read about cancan gem. But i could not understand.
I want to achieve this:
User only able to view/edit/delete his own list.
User only able to view/edit/delete his own profile(user class).
I don't implement user level right now. No guess or admin.
How to use before_filter method in list and User controller with current_user instance?
Since you are defining current_user in the application controller, this is easy. You can use before_filter like this in the Users controller:
class ItemsController < ApplicationController
before_filter :check_if_owner, :only => [:edit, :update, :show, :destroy]
def check_if_owner
unless current_user.admin? # check whether the user is admin, preferably by a method in the model
unless # check whether the current user is the owner of the item (or whether it is his account) like 'current_user.id == params[:id].to_i'
flash[:notice] = "You dont have permission to modify this item"
redirect_to # some path
return
end
end
end
###
end
You should add a similar method to UsersController to check if it is his profile, he is editing.
Also, have a look at Devise which is the recommended plugin for authentication purposes.
For this I'd not use devise. It's way to much for this simple use.
I'd make a seperate controller for the public views and always refere to current_user
Remember to make routes for the actions in the PublicController
class PublicController < ApplicationController
before_filter :login_required?
def list
#list = current_user.list
end
def user
#user = current_user
end
def user_delete
#user = current_user
# do your magic
end
def user_update
#user = current_user
# do your magic
end
# and so on...
end
I have a root_path on my Rails application that is not user-protected i.e. it's a simple portal homepage, with a login form.
After the users log in, I'd like it to go to dashboard_path.
I've done this:
def signed_in_root_path(scope_or_resource)
dashboard_path
end
This apparently should be used when an user signs in, and I don't want it to go to the root_path, while still keeping the user going back to a previous page if it tries to hit a restricted area and it's either timed out or not logged in.
i.e.:
restricted_page -> login -> restricted_page_but_logged_in
I don't want to change this behavior, and that's why I haven't used after_sign_in_path, but want to redirect it if it's on root_path, or any route that doesn't require user authentication.
My problem is that this is not working. After signing in, I'm getting redirected back to root_path, which I believe is because of after_sign_in_path getting triggered before.
Is there any way to do this? Thanks!
Edit: This works the second time I log in, i.e. I go to root_path, log in, gets the flash message stating me that I'm logged in, and enter username and password again on the form on root_path. I successfully get redirected to dashboard_path. Still, not quite the behavior I want.
Just a thought
You can define two root url one for signed in url which will point to dashboard and second for non signed in users which will point to login page
define different root url based on some constraints
in routes.rb
root url for signed in users
constraints(AuthenticatedUser) do
root :to => "dashboard"
end
root url for non signed in users
root :to=>"users/signin"
then create class AuthenticatedUser in lib/authenticated_user.rb
class AuthenticatedUser
def self.matches?(request)
user_signed_in?
end
end
now if user is signed in root_url will point to dashboard else it will point to signin page
Your can also create two roots using(did not tested it yet)
root :to => "dashboard", :constraints => {user_signed_in?}
root :to => "users/signin"
more on constrains
http://edgeguides.rubyonrails.org/routing.html#request-based-constraints
Note
The priority of url is based upon order of creation,
first created -> highest priority resources
It sounds like you're over complicating the issue. If you get into overriding routing variables it just leads to headaches down the line. I would recommend using a before filter to require a login and use the except param or skip that before filter for your landing page if you're using a separate controller. As an example:
class ApplicationController < ActionController::Base
before_filter :require_login, :except => :root
def root
# Homepage
end
protected
def require_login
redirect_to login_path and return unless logged_in?
end
end
(Make sure you have logged_in? defined)
If you are using a separate controller it will look something like this:
class HomepageController < ApplicationController
skip_before_filter :require_login
before_filter :route
protected
def route
redirect_to dashboard_path and return if logged_in?
end
end
Regarding proper routing after a login, that would come down to what you're doing when you're creating your session. Regardless, this setup should catch anyone that's logged in trying to hit the homepage, and route them to your dashboard and anyone trying to hit restricted content (Anything besides root) and route them to the login_path
You can override the after_sign_in_path_for method without losing the desired behavior.
def after_sign_in_path_for(resource_or_scope)
stored_location_for(resource_or_scope) || dashboard_path
end
I would also put a condition in the root_path action that redirects if current_user exists.
RootController.rb
def index
if current_user
redirect_to dashboard_path
else
super #or whatever
end
end
You could also use a before_filter, if you wanted to add the redirect to many unprotected actions.
I'm using Omniauth and this method has worked well for me. If you're working with a different strategy, I'm sure you could modify it.
After they log in, just redirect them to root_path and it will take them to dashboard_path or whatever other default you set in the routes file below.
Set up your helper and callback methods in the app controller:
# application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def authenticate_user!
unless current_user
redirect_to root_url
end
end
end
Put the before_filter in the restricted controller to catch unauthorized people:
# dashboard_controller.rb
class DashboardController < ActionController::Base
before_filter :authenticate_user!
def index
end
# rest of controller here
end
Add these two items to the route. The first one will not be executed if the condition is not met. If it is, then the top one takes precedence and all root_url calls will go to the dashboard
# routes.rb
YourAppName::Application.routes.draw do
root :to => 'dashboard#index', :conditions => lambda{ |req| !req.session["user_id"].blank? }
root :to => 'static_page#index'
# the rest of your routes
end
I think you're solution is more complex than necessary. Why don't you just do something simple like this on the action that the login form is posted to:
def login
// logic to check whether login credentials authorize user to sign in
// say 'user_signed_in?' is boolean to determine whether user has successfully signed in
redirect_to(user_signed_in? ? dashboard_path : root_path)
end
The login form is on every pages (top/sidebar) or you have a login page?
If is on every pages, you can use request.referrer to know where the user came from.
You can control this with a before_filter on your application controller.
I'm not sure whether or not you're using an after_filter or before_filter somewhere for your redirects but you might be able to use a skip_filter in your login controller. Then put in your custom redirect as a filter within that controller.
Skip before_filter in Rails
Is it possible to redirect users to different pages (based on role) after signing in with Devise? It only seems to redirect to the root :to => ... page defined in routes.rb
By default Devise does route to root after it's actions. There is a nice article about overriding these actions on the Devise Wiki, https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-on-successful-sign-in
Or you can go even farther by setting stored_locations_for(resource) to nil, and then have different redirects for each action, ie: after_sign_up_path(resource), after_sign_in_path(resource) and so on.
You can simply add this method to your application controller:
def after_sign_in_path_for(resource)
user_path(current_user) # your path
end
Devise has a helper method after_sign_in_path_for which can be used to override the default Devise route to root after login/sign-in.
To implement a redirect to another path after login, simply add this method to your application controller.
#class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
users_path
end
Where users_path is the path that you want it to redirect to, User is the model name that corresponds to the model for Devise.
Note: If you used Admin as your model name for Devise, then it will be
#class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
admins_path
end
Also, if you generated controllers for Devise, then you can define this in the sessions controller, say, your Devise model is Admin, you can define this in the app/controllers/admins/sessions_controller.rb file to route to the dashboard_index_path:
# app/controllers/admins/sessions_controller.rb'
def after_sign_in_path_for(resource)
dashboard_index_path
end
And in the registrations controller - app/controllers/admins/registrations_controller.rb file:
# app/controllers/admins/registrations_controller.rb
def after_sign_up_path_for(resource)
dashboard_index_path
end
That's all.
I hope this helps
You can use the below code in the application controller or any controller where you need to do the operation:
def after_sign_in_path_for(resource)
users_path
end
https://github.com/heartcombo/devise/wiki/How-To:-Redirect-to-a-specific-page-on-successful-sign-in,-sign-up,-or-sign-out
I used example 1:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :authenticate_user!
protected
def after_sign_in_path_for(resource)
current_user.is_a?(Admin) ? admin_tests_path : (stored_location_for(resource) || root_path)
end
end
To expand on the answer provided by #Asnad Atta
Here is what I ended up using.
def after_sign_in_path_for(resource)
if current_user.role == 'admin'
start_path
else
news_path
end
end
I wanted everyone who logs in to first be pushed to a News page.
After the initial login the Home on the Navbar goes to the start_path (just what I call it in my app). The start_path is also in my root to:
This wonderful after_sign_in_path_for(resource) over rides the root to: path ONLY on the initial Login.
This is exactly what I wanted.
I hope this can help save someone some time.
Scott
Here's what I believe is the answer you are looking for from the devise wiki:
How To: Change the default sign_in and sign_out routes