I am building a rails app and I use Devise for authentication. I want to show a product first page when user comes to www.mydomain.com instead of www.mydomain.com/users/sign_in which is devise default!
I will also want to show a different root view when user is logged in. This feels like a very common use case, is there a easy way to do this? Is this there in documentation or can anyone help me with this?
You can use the authenticated route helper provided by Devise to add a constraint on the routes so that they are only available to logged in users:
Rails.application.routes.draw do
devise_for :users
authenticated :user do
root 'secret#index', as: :authenticated_root
end
root "home#index"
end
The advantage over #Sergio Tulentsev's answer is that this does not cause a redirect or require any additional logic in the controller.
However the reason your app is redirecting to /users/sign_in is most likely that you are using before_action :authenticate_user! in your ApplicationController so that the controller handling the root path is requiring authentication and redirecting to the sign in. You can fix this (while retaining a secure by default setup) by skipping the callback:
class HomeController < ApplicationController
skip_before_action :authenticate_user!
# ...
end
This applies to any controller that should not require authentication. You can skip specific actions by using the only and except options. Just remember that whitelists are more secure than blacklists.
I'd do this way, have a single route for root path:
# routes.rb
root to: 'home#index'
Then check for current user and decide which page to show.
class HomeController < ApplicationController
def index
if current_user
redirect_to some_page_path
# or render some content directly in this response
render :some_view
else # no logged-in user
# same, render or redirect
end
end
end
Related
I am using devise for user registration/login, after the user has successfully signed up, I want to show a page/a dialog box and redirect to another page based on user response. How can I do that?
User Model (By devise)
username
password
Student Model
name
student_id
Teacher Model
name
grade
First_page:
signup signin links
Signup link will show the devise views/devise/registrations/new.html.erb page.
After successful signup, it takes the user to root page. I have defined the root page in routes.rb:
` Rails.application.routes.draw do
devise_for :users
resources :students, :teachers
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
root to: "students#index"
end `
At this point, the application doesn't have any idea who the user is.
So, I want to get the identity information(student/teacher) from the user.
How will I get this information?
Student/Teacher controller:
`class StudentsController < ApplicationController
before_action :authenticate_user!, only: [:new, :create]
def index
#students = Student.all
end
def new
#student = Student.new
end
def create
current_user.create_student(student_params)
redirect_to root_path
end
private
def student_params
params.require(:student).permit(:name, :skypid)
end
end`
After the user has successfully signed in, I want to ask if the user is a student or teacher. Based on what they select, redirect them to a student form page or teacher form page.
How can I do that in rails?
Thank you
You can write a custom after_sign_in_path_for function in your ApplicationController assuming you're using all the default Devise controllers otherwise. Any named path helper or other route that it returns will be where the user is redirected, so you could do something simple like always redirect to a selection page that presents the options and handles the choice on a subsequent action:
def after_sign_in_path_for(resource)
user_type_selection_path # whatever route in your app manages the selection
end
Alternately, you could invoke a custom method on the user model in that function to make a choice right there:
def after_sign_in_path_for(resource)
resource.student? ? student_path : teacher_path
end
You could hybridize these of course as well to do the latter when the selection has already been made and redirect otherwise, with something similar to the following:
def after_sign_in_path_for(resource)
if resource.user_type_chosen?
resource.student? ? student_path : teacher_path
else
user_type_selection_path
end
Bear in mind that none of those functions or paths are real, since I can't be more specific on the information you've provided, but hopefully this gets you headed in the right direction. The after_sign_in_path_for hook is your primary tool here, unless you get into the world of overriding the default devise controllers and interrupting the usual workflow there to accommodate this step, which doesn't seem strictly necessary by your description.
It seems the most close method is to hook the devise model User to override after_database_authentication method.
But within that model method I was unable to send a flag to the controller which handles the root route. (Somehow current_user could not reach atr_accessor enabled parameter).
So wrapping up, what is the most rails way to differentiate devise authentication, if it is made by providing a password or if it is made by a cookie?
In the rails MVC model the model is not session aware. Your User model thus does and should not know if there is signed in user.
If you want to differentiate from when the user is redirected from the sign in vs other hits on the root path you can set a value in the session:
# routes.rb
devise_for :users, controllers: { sessions: 'sessions' }
# app/controllers/sessions_controller.rb
class SessionsController < Devise::SessionsController
def create
super do
session[:just_signed_in] = true
end
end
end
# this would be whatever controller you have that handles the route path
class HomeController
after_action :cleanup!
def index
if session[:just_signed_in]
# ...
else
# ...
end
end
private
def cleanup!
session.delete(:just_signed_in)
end
end
Another way to do this is by adding a query param to the redirect path after sign in.
Here, whenever i try to open localhost:3000 it automatically changes into localhost:3000/users/sign_in and login form gets appear. But i don't want this login form when i enter localhost:3000. I simply want all content except form at first and when i press login or signup links, corresponding form should only be displayed.
Here is code for my application controller that renders my home page:-
protect_from_forgery with: :exception
before_action :authenticate_user!
layout :layout_by_resource
protected
def layout_by_resource
if !user_signed_in?
"home"
else
"dashboard"
end
end
so can anybody help me to solve this problem. I am using devise gem for user login.
Either (1) your root url is set to the new session page or (2) you're being redirected to the new session page because your root url is secured by a before_action that requires the user to login.
(1) In config/routes.rb, you may have set your root page to the new session page:
root to: "session#new"
(2) If not, you're being redirected away from your root route.
What does the root route point to? Let's suppose for the purpose of this example that it points to "home#index":
root to: "home#index"
Then you'll want to check in the appropriate controller and controller action (in this case, HomeController#index) to see if you're enforcing authentication there.
Here's what that what might look like:
# app/controllers/home_controller.rb
class HomeController < ApplicationController
before_action :authenticate_user!
def index
end
end
You'll want to make sure that before_action is not applied to the controller action pointed to by the root route:
# app/controllers/home_controller.rb
class HomeController < ApplicationController
before_action :authenticate_user!, except: :index
def index
end
end
Very simple. You use a normal home#index template and redirect to sign_in for devise. if you want form on same page use render.
When you go to localhost:3000 and you are redirected than you have either root to sign_in or some controller code that redirects to sign in when not signed in. Just change that+
A user named Carly is not signed up and arrives at the index page. She signs up and gets automatically redirected to the main dashboard page. The user Carly which is now named CarlyCoolness123 closes her browser and goes to eat dinner. She gets on her PC again, but this time since she only remembers the actual index page called: coolness.com, and not coolness.com/index-dashboard. I want this user to be automatically redirect to the dashboard page if the user is signed in. That means that the user can't access the index page if the user is signed in. How do I do this? I've tried a couple of things, but since I assume that you people here have a lot better understanding with this than me I assume that my mistakes don't need to be included here.
This assumes that you have setup Devise correctly and you have a Dashboard controller that is responsible for rendering the dashboard view. In your app/controllers/dashboard_controller.rb do this:
class DashboardController < ApplicationController
before_filter :authenticate_user!
...
end
Then in your config/routes.rb file add the following:
resources :dashboard
authenticated :user do
root :to => "dashboard#index"
end
root :to => redirect("/users/sign_in")
If you have an index view for the dashboard, accessing the root of your app should automatically render it (if the user is signed in). If the user is not signed in, it will redirect to devise default sign_in view (if you haven't redefined it)
I prefer to use the below method, as it seems counterintuitive to me to have multiple root paths [read: "I was getting an error when I tried the other mentioned ways, so I tried something else."].
I think this works better and is actually what #MFCS was originally asking, since it doesn't make the root point somewhere else conditionally. Instead redirects to a different path when the signed in user visits the root_path:
config/routes.rb:
root to: 'welcome#index'
app/controllers/welcome_controller.rb:
class WelcomeController < ApplicationController
def index
if user_signed_in?
redirect_to dashboard_path
end
end
end
I prefer this, since the dashboard will still have the dashboard url show up in the browser (instead of the root url), although this may not be the preference of others, and also depends on your preferred user experience.
EDIT: I made a mistake in the code, referencing the DashboardController instead of the controller of the root resource.
While this is an old post, I kinda ran into a similar problem and since I didn't use devise and use bcrypt instead, then this was the solution. I defined a current_user in my application controller and also authorize.
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
def authorize
redirect_to 'home/index' unless current_user
end
I then route all people to their secure dashboard and run before filter on all secure controllers like so before_filter :authorize, then if they fail, they are routed to the home page. Hope this helps someone in the future!
I need a 'Contact' link for both authenticated and unauthenticated users that will send them to
new_user_widget_path(current_user)
This doesn't work for unauthenticated users of course because there is no current user. The method I've been using to solve this problem is to have two routes:
resources :widgets, only: :new
resources :users do
resources :widgets
end
The only purpose of the first route is to provide redirection in the unauthenticated case, and then redirect that user to the new widget page once he signs in.
class WidgetsController < ApplicationController
before_filter :authenticate_user!
def new
redirect_to new_user_widget_path(current_user)
end
end
This works perfectly well, but I'm curious, has anyone come across a more elegant solution to this problem?
I don't think there is anything particularly wrong with your approach. An alternative is to have a guest user. In my app, if a user requests a page with needs authentication, I redirect them to the login page, and then redirect them to the page they were trying to go once they log in. If you have a system like this, you can check if the guest id is in the full path and replace it with the now logged in current_user id.
User is not signed in and but you want proper redirection. In the view you can do this:
new_user_widget_path(current_user || "_")
And then add this to application_controller.rb
def stored_location_for(resource_or_scope)
if path = super
prefix = polymorphic_path(current_user.class)
path.gsub!("#{prefix}/_", "#{prefix}/#{current_user.id}")
end
end
which replaces the underscore with correct ID.
This is implemented on top of Devise's stored_location_for method but it can easily be adapted to other authentication setups.