-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Bug reports
One of the best features of Devise is its ability to provide a complete set of functionality for multiple types of Users.
Now we could combine Devise with some role ability gem so that we have a database attribute that checks which type of user is signing in. There are advantages to this approach.
However, creating a single devise model for each type gives your application more flexibility and feels natural. By doing this we are able to have sign up, log in, forget password, mailers, confirmation instructions all specific to that type of user with the namespace included.
This stackoverflow answer inspired the new version for this page: https://stackoverflow.com/questions/37145991/using-devise-for-multiple-models Credit and thanks go to: https://stackoverflow.com/users/4509585/pyfl88
After following the docs on how to get Devise up and running (don't ignore any of the steps) do:
For this example we'll be creating a User model and a Admin model
rails generate devise user
rails generate devise admin
devise_for :users, path: 'users'
# eg. http://localhost:3000/users/sign_in
devise_for :admins, path: 'admins'
# eg. http://localhost:3000/admins/sign_in
# config/initializers/devise.rb
config.scoped_views = true
# run
rails g devise:views users
rails g devise:views admins
This will become important in Section 6
rails generate devise:controllers users
rails generate devise:controllers admins
devise_for :users, path: 'users', controllers: { sessions: "users/sessions" etc....}
devise_for :admins, path: 'admins', controllers: { sessions: "admins/sessions" etc....}
6. Fix cross model visits (fancy name for: Users can visit admins login and viceversa and mess up your auth tokens)
ActionController::InvalidAuthenticityToken
happens when a logged in Devise model (eg. User) logs in, in the same browser, as another Devise Model (eg. Admin)
A technical issue with this type of setup, however, is that a signed in devise user will be able to access sign in and login pages of another devise user. This is definitely not a desired result.
As a solution, a simple concern can be implemented. Here it is called, Accessible
.
# ../controllers/concerns/accessible.rb
module Accessible
extend ActiveSupport::Concern
included do
before_action :check_user
end
protected
def check_user
if current_admin
flash.clear
# if you have rails_admin. You can redirect anywhere really
redirect_to(rails_admin.dashboard_path) and return
elsif current_user
flash.clear
# The authenticated root path can be defined in your routes.rb in: devise_scope :user do...
redirect_to(authenticated_user_root_path) and return
end
end
end
Here we are redirecting and returning any user that is trying to access pages that are not specific to the type of user they are. Including this concern in your Sessions and Registrations Controller while performing this check as a before_action on your new methods is one simple way to setup multiple users for devise.
# eg. ../controllers/admins/sessions_controller.rb
# eg. ../controllers/admins/registrations_controller.rb
# eg. ../controllers/users/sessions_controller.rb
# eg. ../controllers/users/registrations_controller.rb
include Accessible
You must skip_before_action
for the destroy
action in each SessionsController
to prevent the redirect to happen before the sign out occurs.
class Admins::SessionsController < Devise::SessionsController
include Accessible
skip_before_action :check_user, only: :destroy
# ...
end
You must also skip_before_action
for the edit
, update
, destroy
, and cancel
actions in each RegistrationsController
to allow current users to edit and cancel their own accounts. Otherwise they will be redirected before they can reach these pages.
class Admins::RegistrationsController < Devise::RegistrationsController
include Accessible
skip_before_action :check_user, except: [:new, :create]
# ...
end
Doing this won't allow other types of users to access these pages, as if another type of user is signed in and tries to access the admin user edit page for example, they'll be redirected to the admin user login page, which will then redirect to your specified route thanks to Accessible
.
Devise uses devise.en.yml
to determine which flash messages to display based on the action a user is performing. We can customise these messages per scope.
en:
devise:
confirmations:
admin_user:
confirmed: "Your ADMIN email address has been successfully confirmed."
confirmed: "Your email address has been successfully confirmed."
If an admin_user
confirms their email address, in this instance they will see a custom flash message. All other user types will see the default message.