Pedro Nascimento
Pedro Nascimento

Reputation: 13886

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.

Upvotes: 11

Views: 9266

Answers (8)

Naveed
Naveed

Reputation: 11167

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

Upvotes: 23

jstim
jstim

Reputation: 2432

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

Upvotes: 1

Matthew
Matthew

Reputation: 5825

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

Upvotes: 4

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

Upvotes: 0

Aaron Renoir
Aaron Renoir

Reputation: 4381

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.

Upvotes: 2

yekta
yekta

Reputation: 3433

You can control this with a before_filter on your application controller.

Upvotes: 0

Fernando Almeida
Fernando Almeida

Reputation: 3174

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.

Upvotes: 0

Batkins
Batkins

Reputation: 5706

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

Upvotes: 0

Related Questions