Conditional Route on sign_out using Devise with Rails - ruby-on-rails

I am working on an app in which there is a model User with roles member and Admin.
As per requirement, I have to made two separate login pages for Admin and Member.
with http://localhost:3000/admin/admin_login
it goes to admin login page and with
http://localhost:3000/users/sign_in
it goes to member login page.
Just after login I route them according to their roles to Admin panel or simple website for members.
But at time of logout both goes to
http://localhost:3000
but I want admin to go to http://localhost:3000/admin/admin_login,
while http://localhost:3000 is fine for member's logout.
Is there a way to see User's Role at time of Sign_out and route them accordingly.

You can define a after sign out path according to resource in application controller..
class ApplicationController < ActionController::Base
private
# Overwriting the sign_out redirect path method
def after_sign_out_path_for(resource_or_scope)
root_path
end
end
In after_sign_out_for_for method you can check user role and redirect to
For more details visit devise wiki After Sign out path in devise

I think you can try like this
Just save whatever path you want to redirect user after sign_out based on the role after login in session and use that session in after_sign_out_path_for method
after_filter :store_location
def store_location
return unless session[:login_url].blank?
session[:login_url] = current_user.admin? ? admin_path : other_user_path
end
def after_sign_out_path_for(resource)
session[:login_url] || request.referer || root_path
end
there are one other way just overwrite devise sessoin controller and redirect user from there based on role
# routes.rb
devise_for :users, :controllers => { :sessions => "sessions" } # etc
# sessions_controller.rb
class SessionsController < Devise::SessionsController
def destroy
#login_path = set path in a variable based on user role before sing_out
#code to sign out
#
redirect_to login_path
end
end

Are the pages that they are clicking on the logout links different for admins and users? If so, you could check the request.referrer property in your after_sign_out_path method and route accordingly.
BTW if you are routing them to the right pages after logging in then why do you need separate login pages? you may want to just have one login page for simplicity.

You can also leave it config.sign_out_via =: get => config/initializers/devise.rb is not the best way but solved for me.

Related

How do I generate a temporary page like confirmation page in rails?

I am using devise and want to redirect users to a confirmation page upon signup, this is what I am doing right now:
users/registrations_controller.html.erb
class Users::RegistrationsController < Devise::RegistrationsController
def confirm_email
end
private
def after_inactive_sign_up_path_for(resource)
users_confirmyouremail_path
end
end
config/routes.rb
devise_scope :user do
get 'users/confirmyouremail' => 'users/registrations#confirm_email'
end
I have no problem with redirecting the page after signup. However, I think it is quite weird that anyone can visit the page with url like `host.com/confirmyouremail' and see the confirmation page. Are there any ways I can write a route that will use random code that is allow only for one time visit? Thanks in advance.
Maybe something like this:
before_action :authenticate_user!
def confirm_mail
redirect_to root_path if current_user.confirmed
...
end
You are storing in the database if the user has already confirmed his account. If his account is confirmed then he won't be able to access this page. You can redirect to whatever page you want. A user without any account won't be able to access this page because of the before action
In case the user is not logged in when he accesses this confirm_mail page you have different possibilities. You could use a session or a cookie:
# after sign up:
session[:confirm] = true
# alternatively a cookie
cookies[:confirm] = true
Then in the confirm mail action:
def confirm_mail
if session[:confirm].blank? # or cookies[:confirm].blank?
redirect_to root_path
end
# otherwise delete the field from the session
session.delete(:confirm)
# alternatively the cookie
cookies.delete(:confirm)
end
Another way would be by using a Token. You create a new model like ConfirmMailToken. Then on sign up you create a new token and redirect the user to the confirm page with the token as a URL param. Then in the confirm_mail action you check if a token is available and delete it if it is. This way you ensure that the page is only shown after redirect.

Devise with Confirmable - Redirect user to a custom page when users tries to sign in with an unconfirmed email

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

Redirect user after log in only if it's on root_path

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

How to pass through parameters during Devise login

How do I pass parameters during the Devise sign-in process?
I have a collection of users, each with their own profile page. I want them to be able to view their own profile page but none of the other users' profile pages, unless they're an admin in which case they have access to everything in the application.
I created a Users controller, and applied the following code to the beginning of the controller. I replaced looking up by ID with their username, so /users/username to access a profile:
filter_access_to :all do
current_user == User.find_by_username(params[:id]) or
has_role? :admin
end
And I specified in routes.rb:
root :to => "users#show"
When the user accessed http://appli.cat.ion they would be prompted for login credentials, then be redirected to the root_path which is the show action of the Users controller. Unless I can pass along the id param during the sign-in process, this will result in an endless redirect loop. Is there something in Devise that helps with this problem?
I believe the following two Devise methods can help:
after_sign_in_path_for(resource)
and
stored_location_for(resource)
I've overriden after_sign_in_path to the following:
def after_sign_in_path_for(resource)
user_root_path(:id => current_user.username)
end
which provides the desired effect: user logs in, gets redirected to their profile page, but only if I return nil in stored_location_for like:
def stored_location_for(resource)
nil
end
Again, my goal is to redirect a user to their profile page if having navigated to (http)://appli.cat.ion/ directly if they've signed-in. If a user navigates to (http)://appli.cat.tion/otherresources then I want to sign the user in, then redirect them to the page they requested. If I return nil in stored_location_for then the user will ALWAYS get redirected to their profile page no matter what, which is undesirable at best, and unusable at worst. I think that if I navigate to the application sign-in directly then stored_location would return nil, and upon signing in be directed to my profile page, but it is just redirecting me to the root_path.
I think you can create a profile action within the UsersController which does a redirect to the user show page, then mark it as user root.
In UsersController:
class UsersController < ApplicationController
...
def profile
redirect_to user_url(:id => current_user.username)
end
...
end
In routes:
resources :users do
get 'profile', :on => :collection, :as => :user_root
end
And don't need to use the after_sign_in_path hook.

How to redirect a user after registration when using Devise?

I am using Rails 2.3 and Devise to handle user registration / authentication.
I need to redirect a user to an external 3rd party website immediately after a user signs up for an account. Been looking in the code & online but cannot see how to do this.
How can I alter the devise flow to redirect the user?
The answer listed as the "correct" answer specifically refers to after sign_in... If you want to do redirect a user after sign_up you need to override the following:
def after_sign_up_path_for(resource)
"http://www.google.com" # <- Path you want to redirect the user to after signup
end
Full details can be found on the wiki.
Add to your Application Controller
# Devise: Where to redirect users once they have logged in
def after_sign_up_path_for(resource)
"http://www.google.com" # <- Path you want to redirect the user to.
end
Here is the list of Devise helpers you can use http://rdoc.info/github/plataformatec/devise/master/Devise/Controllers/Helpers
I hope that helps =)
If you are using Devise's confirmations (meaning the user is not activated immediately after they sign up), you need to overwrite the after_inactive_sign_up_path_for method.
# controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def after_inactive_sign_up_path_for(resource)
"http://somewhere.com"
end
end
Make sure to tell devise to use your RegistrationsController.
# config/routes.rb
devise_for :users, :controllers => {:registrations => 'registrations'}

Resources