I have a standard devise implementation and throughout other models there seems to be no redirect if the session is expired, leaving the user with a error message.
For example on the user profile page if not logged in it will just show an error because the current_user does not exist.
Do i set in each model stating to authorise. Or a better solution, can I set it in the app controller and do it application wide and simply set any public pages wherever necessary?
If you want a particular controller to check if a user is logged in then you want to use Devise's authenticate_user! function. See example below
class StuffController < ApplicationController
before_filter :authenticate_user!
def index
..... more implementation
end
end
If the user is not logged in they will be redirected to the login form and then to the required page after a successful login
Use a before filter such as before_filter :authenticate_user!. Place this in your application controller. authenticate_user! is a devise helper so if you want custom behavior you can overload the method or simply write your own filter using their user_signed_in? helper method.
Related
I am using Devise gem for Authentication and ActiveAdmin gem which also uses Devise gem as dependency. I want to enable 2FA for ActiveAdmin, that's why I want to over ride after_sign_in_path_for method. I am able to over ride the method in ApplicationController but doesn't sounds right to me because this will affect our normal login as well. Is there any way to over ride after_sign_in_path_for only for ActiveAdmin. Currently this is how I am doing
class ApplicationController < ActionController::Base
def after_sign_in_path_for
end
end
If I can't only over ride only ActiveAdmin controller then how to deal with normal login without 2FA and ActiveAdmin login with 2FA. Something like this.
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
if resource.instance_of?(AdminUser)
redirect_to setup_2fa_path
else
super # <-------- is this possible or is this correct ?
end
end
end
There must be a way where I can only over ride after_sign_in_path_for for ActiveAdmin. Or do I need to over ride ActiveAdmin::Devise::SessionsController class in initializer? Putting controller login inside initializer also don't feel right to me.
UPDATE
I noticed there is one more problem, when control reaches after_sign_in_path then current_admin_user is already set. This is problem because user can skip setting up 2FA and type path in browser like localhost:3000/admin and they are in because there is current_admin_user. I can set current_admin_user to nil in after_sign_in_path but I am not sure if that will open door to other type of attacks ? How should I deal with this ? or shall I try to over ride different method ? I don't know if there is something like before_sign_in_path_for or something else.
An approach is to open up and extend the Devise::SessionsController within ActiveAdmin. I managed to implement 2fa with a one time password based on code discussed at https://medium.com/#acesubido/adding-two-factor-authentication-in-activeadmin-2ed134b60042 and https://medium.com/#acesubido/part-2-adding-two-factor-authentication-in-activeadmin-cc5eab67057c
Also: https://blog.kiprosh.com/adding-two-factor-authentication-2fa-for-activeadmin-auth-in-a-ruby-on-rails-web-application/ can be of inspiration, I chose for the first setup with a 2 step process. Also you may want to consider a before_action in you ApplicationController that allows for users to setup their 2fa after logging in:
def check_2fa
return if current_admin_user.nil?
redirect_to user_dashboard_two_factor_new_path \
if current_admin_user.force_2fa && !current_admin_user.otp_required_for_login
end
Good luck.
I have a Rails 3.2.22 app running in production for +1 year which uses Devise to authenticate users.
I'm trying to implement token authentication, so I can send transactional e-mails with URL params that can log in the user automatically, using a Gem named Simple Token Authentication https://github.com/gonzalo-bulnes/simple_token_authentication
After following all the instructions, I replaced before_filter :authenticate_user! in my controllers with acts_as_token_authentication_handler_for User.
The gem has integration with and a default fallback to Devise, so devise doesn't need to be called in the controllers anymore; if the token is missing from the params (or wrong), Devise will take over.
In my tests, if I add this line to ApplicationController, everything works fine and I can log in users using the authentication_token= secret the gem generates.
But I don't need auth for ApplicationController, I need it for other controllers (like DashboardController), url being /dashboard
If I put acts_as_token_authentication_handler_for User in that controller (replacing Devise's call), I get the most bizarre of situations.
Using binding.pry, I can confirm that current_user is correctly set during the loading of the template.
But there comes a point in the template where it uses #last_emails, which is defined inside a method in ApplicationController.
Using binding.pry, I can confirm current_user is nil there.
This is the code:
class DashboardController < ApplicationController
layout 'material'
acts_as_token_authentication_handler_for User
And in ApplicationController:
class ApplicationController < ActionController::Base
layout 'omega'
before_filter :populate_last_contacts_for_menu
private
def populate_last_contacts_for_menu
if current_user
#last_contacts = Contact.where("user_id" => current_user.id).where('blocked != ? or blocked is null', true).last(10).reverse
end
end
Funny thing is: using binding.pry, like I said, I can check that current_user is defined in the template (which means sign_in was a success). It even is defined in the better errors console. But, if I go to homepage, I see that user is not logged in ...
I've looked all over the web for this: read all the issues inside the Gem's github and all posts in SO about current_user being nil, but no light at all.
My devise_for :users is not inside any scope in routes.rb and, as I said, I have many calls to current_user all over the app and this is the first time I have issues with Devise.
When you call the acts_as_token_authentication_handler_for directive in the DashboardController it declares some before_filters for the controller to authenticate a user.
But the problem is that when you inherit rails controllers, at first, filters of a parent controller are executed, then filters of a child controller.
The parent controller is ApplicationController. At the moment when it's populate_last_contacts_for_menu filter is called, the user is not authentacated, because the authenticating filters given by the acts_as_token_authentication_handler_for directive have not called yet, they are declared in the child controller.
Possible solutions:
1) Try to append the populate_last_contacts_for_menu filter:
append_before_filter :populate_last_contacts_for_menu
I am not sure it will work in your case, but you can try and find it out.
2) Call the acts_as_token_authentication_handler_for directive in the ApplicationControoler and somehow skip it for the controllers that don't need it. (I don't like this way, but it may help if the first one will not work. )
3) Move the populate_last_contacts_for_menu filter logic into helpers. I think it is the best solution. This logic doesn't belong to a controller. When requests are not 'get', this filter executes for nothing, because you don't need to render views in that case.
module ApplicationHelper
def last_contacts
#last_contacts ||= if signed_in?
Contact.where("user_id" => current_user.id).where('blocked != ? or blocked is null', true).last(10).reverse
else
[]
end
end
...
end
# View:
<% if last_contacts.present? %>
....
<% end %>
I have three possible permissions for a User to be in my Rails app, they are User.is_admin, User.is_school, and User.is_security. Based on the nature of my app I need to have a separate home screen for each of these users that do radically different things, which I have working. The problem that I'm having has to do with how Devise auto redirects to root_path after login for all users, regardless of the permissions I have set.
I generated the Devise Sessions controllers into the Users namespace and I have overwritten it to default to my controller, but now when I try to do a redirect, based on the conditional permissions, I get a DoubleRenderError (The obvious reason being that Devise is redirecting elsewhere when creating the session).
I have tried running it as an after_action and even tried overwriting the after_sign_in_path_for method, as per the direction of the Devise docs on the matter, but I still can't get it working. Any help would be appreciated, thank you!
You can do something like this
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
if resource.role == 'admin'
admin_root_path
else
user_root_path
end
end
end
you can read more about this https://github.com/plataformatec/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-sign-in
I use Devise gem. It's not configured to be :confirmable, however I want an user not to log in automatically right after a registration process. For some reason, now they are logged in automatically which exactly the opposite of what I want.
So how do I do that?
https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-on-successful-sign-up-(registration)
Basically, follow the above steps. In your after_sign_up_path_for method, you can call
sign_out resource
then redirect them to whatever page you want them to go to (perhaps root, or the login page).
The user is logged in via the sign_up method called by devise; it's standard procedure -- many websites log in users immediately after they sign up. Most that don't require confirmation first, so your use case it somewhat atypical. Still, doing the method I described above should do what you want, without having to implement confirmable or doing any membership approvals.
Signing in the user after sign up is achieved by the sign_up method in Devise::RegistrationsController that gets called by the [create][1] action. You can simply override this method with an empty one to prevent signing in. That worked for me!
In app/controllers/users/registrations_controller.rb:
class Users::RegistrationsController < Devise::RegistrationsController
def sign_up(resource_name, resource)
end
end
Note: replace users with your table name.
Extra:
If you allow only admins to add new users , or perhaps other admins, and use the create action to add them, this previous method will also give you the benefit of not having to sign out. You can customize what page to be redirected to after signup by overriding the after_sign_up_path_for method in app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
def after_sign_up_path_for(user)
'/users/show' # replace with the path you want
end
I'd like to set a session variable once a user signs in based on a certain field in the User table. I don't want to have to create a custom Devise controller if I don't have to. Is there a way? Or will I have to go the custom controller route?
There is a callback after_sign_in_path_for, you can add it in your ApplicationController
protected
def after_sign_in_path_for(resource)
session[:domain_prefix] = current_user.domain_prefix
user_path(resource)
end
Dont forget return the path in the last line of method, otherwise the callback will redirect the request to content of session[:domain_prefix]
How about this one:
The first resource I'd look at is http://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-on-successful-sign-in. Also, check out How to redirect to a specific page on successful sign up using rails devise gem? for some ideas.
You can do something like:
def after_sign_in_path_for(resource_or_scope)
session[:account_type] = current_user.account_type
end
You can implement this method in your ApplicationController or in a custom RegistrationsController.