I’m tired of spending loads of time creating user authentication systems with permissions or swimming against the current to customize what’s available. There’s great open source stuff out there but until now, I haven’t gotten the full package with really easy customization.
The Devise and CanCan combo for user authentication and permissions in Rails is my combo of choice.
With Devise and CanCan, you can create a customized authentication and registration process in 15 minutes, and spend another 15 minutes implementing roles and permissions.

It’s pure beauty.
Note that the code here uses Rails 3. The difference in Rails 3 and Rails 2 code for this purpose should be minimal, but please refer to the documentation for differences.
Let’s start with authentication using devise.
Step 1 – Installation
gem install devise rails generate devise:install rails generate devise user
Step 2 – Configuration
Configuration is super easy with Devise. Just choose which of the 11 available modules you would like to include in your authentic model (most up-to-date list here):
- Database Authenticatable: encrypts and stores a password in the database to validate the authenticity of an user while signing in. The authentication can be done both through POST requests or HTTP Basic Authentication.
- Token Authenticatable: signs in an user based on an authentication token (also known as “single access token”). The token can be given both through query string or HTTP Basic Authentication.
- Oauthable: adds OAuth2 support
- Confirmable: sends emails with confirmation instructions and verifies whether an account is already confirmed during sign in.
- Recoverable: resets the user password and sends reset instructions.
- Registerable: handles signing up users through a registration process, also allowing them to edit and destroy their account.
- Rememberable: manages generating and clearing a token for remembering the user from a saved cookie.
- Trackable: tracks sign in count, timestamps and IP address.
- Timeoutable: expires sessions that have no activity in a specified period of time.
- Validatable: provides validations of email and password. It’s optional and can be customized, so you’re able to define your own validations.
- Lockable: locks an account after a specified number of failed sign-in attempts. Can unlock via email or after a specified time period.

I chose 5 of the 11 modules and configured with the following code:
# In your model class User < ActiveRecord::Base devise :database_authenticatable, :confirmable, :recoverable, :rememberable, :trackable, :validatable end # In your migration create_table :users do |t| t.database_authenticatable t.confirmable t.recoverable t.rememberable t.trackable t.timestamps end # In your routes devise_for :users
Step 3 – Use It!
# In your controllers before_filter :authenticate_user!, :except => [:some_action_without_auth] # Access Current User def index @things = current_user.things end
This simple modular approach to authentication is hot. Devise also makes it really easy for you to customize views. The out-of-the-box views are great for prototyping, but if you need more, just generate the views and edit them:
rails generate devise:views
Devise will generate all of the views it is using and place them in an app/views/devise directory. Now you have complete control over your views.
The next thing you might want to do is customize your controllers. This is a bit more tricky with devise and we’ll get to that in a minute. Right now I want to touch on permissions and then I’ll tie it all together.
Let’s consider an example where your website is in Alpha/Beta or maybe an internal tool. You want to restrict user registration to only an administrator. Enter CanCan created by Ryan Bates.
CanCan
CanCan is a great gem for implementing model permissions. The main reasons I chose CanCan are:
- The code written to check permissions is very readable
- The code written to declare permissions is very concise and readable
- It keeps permission logic in a single location so it is not duplicated across controllers, views, etc.
- Aliasing actions (read = index and show) creates more concise and readable code
Ryan Bates has a great screen cast on using CanCan here, but I do not recommend using his roles mask method (mentioned in the screen cast). It certainly works but it’s bad database design and you will feel the pain later.
After you install CanCan (instructions here), I recommend you set up a typical users HABTM roles relationship. So you end up with migrations that look like this:
class CreateRoles < ActiveRecord::Migration def self.up create_table :roles do |t| t.string :name t.timestamps end end def self.down drop_table :roles end end class UsersHaveAndBelongToManyRoles < ActiveRecord::Migration def self.up create_table :roles_users, :id => false do |t| t.references :role, :user end end def self.down drop_table :roles_users end end
And your models look like this:
# User Model class User < ActiveRecord::Base has_and_belongs_to_many :roles .... # Role model class Role < ActiveRecord::Base has_and_belongs_to_many :users end
The next step is to create your Ability class that will define permissions. Mine looks like this:
class Ability include CanCan::Ability def initialize(user) user ||= User.new # guest user if user.role? :super_admin can :manage, :all elsif user.role? :product_admin can :manage, [Product, Asset, Issue] elsif user.role? :product_team can :read, [Product, Asset] # manage products, assets he owns can :manage, Product do |product| product.try(:owner) == user end can :manage, Asset do |asset| asset.assetable.try(:owner) == user end end end end
Most of this is application specific but you can see some conveniences right away. For example, the super admin role “can manage all”. That line is saying “If the user has the super_admin role, he may perform any action on any model.” Easy enough. Also notice that the product team can “read” products and assets. This means that they can access the index or show action of either of those models. You can pass a block to the can method for more complicated permission checks, but that is beyond the scope of this post and pretty easy to figure out.
Let’s take a look at the role method. I store role names as CamelCase strings in the database but I access them with underscores which is more ruby like. The method looks like this:
def role?(role) return !!self.roles.find_by_name(role.to_s.camelize) end
Tying it all together
Now let’s go back to the situation I mentioned earlier – you want to protect user registrations. This requires us to use CanCan to check for permissions but customize the Devise Registrations controller to restrict access.
One way to do this is to copy the devise controllers into your controllers directory and start customizing. That may be the best way to go and it’s certainly an obvious path, but all I want to do restrict registration. Should I really have to re-implement the registrations controller to do that? For now, I will not. It might make sense when there are more customizations. Instead I inherit from the Devise Registrations controller. Here are the steps:
Step 1 – Create the controller
$ mkdir app/controllers/users
$ touch app/controllers/users/registrations_controller.rb
Step 2 – Add the custom functionality
class Users::RegistrationsController < Devise::RegistrationsController before_filter :check_permissions, :only => [:new, :create, :cancel] skip_before_filter :require_no_authentication def check_permissions authorize! :create, resource end end
The check permissions method is really simple. It calls the CanCan method, authorize!, and checks if the current user can create users. We use resource here because devise uses resource to refer to the model that can be authenticated. Also notice how I removed the require_no_authentication filter, a Devise filter which allows access to actions without authentication.
Step 3 – Tell your routes to go to the new controller
# replace devise_for :users with: devise_for :users, :controllers => { :registrations => "users/registrations" }
Step 4 – Handle the CanCan::AccessDenied exception
At this point if you hit the users/sign_up page when not logged in, you will notice that a CanCan::AccessDenied is thrown. This exception is thrown anytime permission is denied so you should customize it to your liking. I put the handler in my ApplicationController:
class ApplicationController < ActionController::Base ... rescue_from CanCan::AccessDenied do |exception| flash[:error] = exception.message redirect_to root_url end ... end
I realize I skipped some steps in here but this post + Devise documentation + CanCan documentation should help you set up authentication with roles and permissions very quickly. Let me know if you have any questions. Enjoy!
UPDATE
A part 2 of this post is now available
If you enjoyed this post, make sure you subscribe to my RSS feed!










63 Comments
There is a very nice devise_invitable module that also allows you to send invitations to users (cutting down on administrative overhead). I’ve had good luck with the version from rymai:
http://github.com/rymai/devise_invitable.git
rymai’s fork works on Rails 3/Devise 1.1.1 and is a fork from Sergio Cambra’s work that hasn’t caught up to Rails 3 yet
@David – Very cool! I will give it a try this weekend. Here is the working link: http://github.com/rymai/devise_invitable
This is really cool. I am now trying the same solution.. thanks for your nice hint of the whole process.
nice ~ thanks !
Tony,
Thank you for this post. It was really helpful. Could you please elaborate on why using Ryan Bates’ Roles approach is “bad database design and you will feel the pain later.” Is it because of the limitation in the number of roles? Were there any other disadvantages that you encountered?
Thanks for the write up Tony, was a nice reminder/walk through on how to get things setup. I’m also a fan of Devise, and have used cancan before. I found this post from searching for discussions of what others are doing for roles as well.
Kenny,
I think he said that for a couple reasons: Ryan’s solution is fairly basic and a bit brittle (and he certainly didn’t say it was THE way to do it or anything, so not at all a criticism). IIRC he used the bitmasked solution, which will break if you have to reorder things a bit, is harder to use if you need/want to store the roles in the db or elsewhere, etc.
Breaking it out as Tony has done lets you define other attributes e.g. Role Display Name or something to show users of your site. In short, quick/dirty way in the screen cast, while something like the method here is probably want you want for a more longer term solution.
@Kenny – I don’t think I could have said it better than Jack
Could you please elaborate how you go about making the view files of Devise with integration of “roles” selection. ie. do you use selection boxes to pick the roles? And how does your code for this look like, and how does Devise know how to use these roles?
I’ve been at this Devise + CanCan business for all day and haven’t been successful so far, I’m about to give up but was hoping you could shed some light on this.
I tried RyanB’s railscasts, but keep getting all kinds of errors even though I’m running a clean install (Rails 3 + Ruby 1.9.2) and am now in the process of adding your solution, but don’t know how to add fields to the registration form to select which roles to add to the user.
Also, I keep getting an error message when I create the app/controllers/users/registrations_controller.rb file, saying it can’t find the view files, but when I copy app/views/devise/registrations/ to app/views/users/registrations/ I again get different errors.
Pleaaaase help :’(
I knew this question was going to come up and I was going to put it in a Devise + CanCan part 2 I am writing up. There is so much awesomeness I couldn’t fit it in one post and I also wanted to learn the libraries a bit more.
Since this is worthy of an entire new post, I’m going to keep my response short and tell you that VERY soon there will be much better instructions on how to really rock out with these two libs.
So what you want is this in your routes:
and you want that controller to inherit from the devise controller:
Then you want your registration views in /app/views/users/registrations
If you get errors at that point, I need more information to help you.
Thanks Tony, great help. I’m going to give it another try today so I’ll let you know how it turns out. In the mean time, I’ve marked this blog to watch for that part II. Looks like it’s going to be a good read!
Thanks again.
Well, I’ve followed your tutorial through the letter and I’ve managed to get it working, yay! Unfortunately, I still don’t know how to add a custom form field in the registration form to select the roles for that user. I managed to get the selection boxes with the available roles, I just don’t know how to insert them in the database and tell devise to leave the field alone.
But I guess that’ll be part of your next blogpost, so I’ll be patiently waiting
First off, thanks for the write up. I found this post while trying to integrate the two gems and it’s been VERY helpful. I’m glad to hear you’re working on a part 2! I would love to see how you would handle setting up a default role for a new member with your current HABTM setup.
@Jean – have you made the attribute accessible? For example, in your user model:
attr_accessible :role_ids@Robert – Glad you enjoyed the post! I will try and address all new questions in the post but if you want a default role, I would consider a couple methods. One method is to add a hidden field in the registration form with the value of the default role. Another is to add a before_save callback or some other ActiveRecord callback to your user model and set the default role in the callback method. More info on ActiveRecord callbacks here.
Thanks for the great post Tony. I have everything setup, however I have two issues.
I can’t access my applications helpers within the devise views. I think this is related to devise being a rails engine within its own module.
And I also can’t access the devise helpers current_user and user_signed_in? from within the cancan ability class. How do I get this to work?
Hey Tony,
Thanks for writing this intro to cancan + devise. I really hope that you’ll explain how to work with these libs if you have indeed copied the devise controllers into your controllers folder.
I think most apps that require devise and cancan are complicated enough that the controllers need to be tinkered with inside the controllers directory.
@prospex – Something is wrong if you can’t access your application helper from your devise view. I can do it just fine. It also makes sense you can’t access “current_user” from a model for the same reason you can’t access any controller methods from your model.
@Gabe – I will be talking about greater levels of customization for controllers in the next post. One thing I’ll be talking about is how to handle a situation where you only want admins adding users (no open registration). If you have anything specific you want me to talk about, leave a comment and I’ll try to cover it if I wasn’t planning to already.
Hey Tony, I’d love to know how to make sure the DB isn’t being accessed constantly to read permissions/roles. I don’t know if that’s already the case, but from Ryan Bates’ technique, I gather that some consideration is needed to speed things when a HABTM relationship is used for defining roles. Thanks a billion!
Interesting post, really looking forward to part 2 and hope it is winging it’s way soon.
I’m looking to start a new project which would not have open registration and would rely on admin users to add other users so part 2 will be exactly what I’m looking for. I’m thinking about using MongoDB rather than a ‘traditional’ RDBMS, and think I will try embedding the Roles table into the User document (table).
Depending on the length of part 2 you could maybe mention testing the solution with the latest tools.
Wow that was easy. All it took was:
before_save :setup_role
def setup_role
self.role_ids = [2]
end
Thanks!
Just a quick note to say thanks for taking the time to write this blog post, it helped me out.
Another suggestion!
None of the docs or tutorials explain how to create an admin account with devise. All I’ve seen is “rails generate devise Admin”. I would like to know how to register something that doesn’t use ‘registrable’. Do I use the console? or perhaps do I use registerable, and then remove that module once i’ve created the admin account?
@Gabe – I haven’t explicitly checked but Rails’ ActiveRecord query cache should be caching results of requesting role information multiple times in one request. If you need further optimization you will need to store the roles in the session or some other cache (memcached).
@Ross – Yes the second post is coming along and should be available end of this week/early next week. Closed registration functionality has been asked for a bunch so I will cover that. Testing would also be great to cover but we’ll see if there’s room. My gut tells me there will be a third post shortly after the second post
@Gabe – To create an initial admin account, you would probably want to use seed data or create the Admin user in the console. As far as creating more admins, you may want a SuperAdmin role that can create new admins. In one of my previous apps I seeded the database with a SuperAdmin so there was always a way to login and add users. I would not use registerable and then remove it. What will happen when you need to bootstrap your application on another server?
Great tutorial. What’s unique about what I’m try to do is have Users, Projects, and then Roles between UsersProjects. users can have different roles for various projects, or none at all. What do you think? Can CanCan accommodate?
I think I’m close. Where does this go?
def role?(role)
return !!self.roles.find_by_name(role.to_s.camelize)
end
@Brett – I believe CanCan can accommodate. CanCan is there to tell you what each role can do, not which role each user has. Assuming you have a typical set of roles for each product, you would define which users have those roles in the database and what those roles “can” do in the ability class.
@Eager2KnowMore – That would go in whatever model has roles. In my case it’s the user model.
Hey Tony,
configuring a Devise controller is only possible on the latest version of Devise?
I can’t figure out how to do with 1.0.8 version, keeps using the gem version whatever I do.
@Vincent – Yes, I believe configuring a devise controller is a recent improvement. In my older apps I end up using available helpers such as “sign_in” and “sign_in_and_redirect.” It’s not my favorite way to use the library but it works.
If I put this code in User model :
before_save :setup_role
def setup_role
self.role_ids = [2]
end
It works but when I sign in with a user which has not the role [2] it is updated.
It seems to work better with :
before_create :setup_role
@fcooker- I noticed that too and ended up putting in a catch ( but forgot to comment here ), so my callback looks like this now:
def setup_role
if self.role_ids.empty?
self.role_ids = [3]
end
end
This tutorial just keeps on giving! Thanks for the time and effort. I have almost everything setup, but one last thing is eluding me. I want to be able to edit other Users. When I’m logged in as super_admin, I can’t seem to access other Users’s attributes and edit them. Any suggestions? How do I generate the /users/:id/edit/ route for this, for example?
Thanks again,
Gabe
@Gabe – There’s a few gotchas. I’ve been slacking but I’m going to write that post up tonight since lots of people want it. It may be more of a collaborative post but we’ll get you started.
Ok Part 2 is up! http://www.tonyamoyal.com/2010/09/29/rails-authentication-with-devise-and-cancan-part-2-restful-resources-for-administrators/
Any chance you could post a sample app on github?
having followed your custom RegistrationsController example, i receive an error Not authorized to create nilclass. when trying to acces the new registration action. if specify authorize! :create, User instead of resource, i am able to access it.
I’ve gone through this tut, great by the way, but I’m having an issue with going to the sign_up page — I’m getting a No Route error, and when I look at rake routes, all I have is:
new_user_session GET /users/sign_in(.:format) {:action=>"new", :controller=>"devise/sessions"}
user_session POST /users/sign_in(.:format) {:action=>"create", :controller=>"devise/sessions"}
destroy_user_session GET /users/sign_out(.:format) {:action=>"destroy", :controller=>"devise/sessions"}
user_password POST /users/password(.:format) {:action=>"create", :controller=>"devise/passwords"}
new_user_password GET /users/password/new(.:format) {:action=>"new", :controller=>"devise/passwords"}
edit_user_password GET /users/password/edit(.:format) {:action=>"edit", :controller=>"devise/passwords"}
user_password PUT /users/password(.:format) {:action=>"update", :controller=>"devise/passwords"}
root /(.:format) {:controller=>"welcome", :action=>"index"}
Any clue why I don’t have sign_up ? I haven’t gone through the second part of the tutorial yet because I’m assuming that I should have the first part working first.
Ok, if I put the :registerable method into the devise statement in the users model, then I get that page, but according to the tutorial, you don’t have this in there, am I just being too literal? Thanks so much – just trying to wrap my head around this
@Jason – Your observation is correct. My website didn’t need public registration so I left that module out.
@Tony — thanks, I figured as much — I just seeded the user and got it up and running, so onto Part 2 — thanks so much for the posts
I setup my system identical to yours, but I’m getting this message when I update a user profile. Don’t quite understand I’m a Noob.
SQLite3::SQLException: no such table: roles_users: SELECT "roles".* FROM "roles" INNER JOIN "roles_users" ON "roles".id = "roles_users".role_id WHERE ("roles_users".user_id = 1 ) AND ("roles"."name" = 'SuperAdmin') LIMIT 1Any suggestions would help. Thanks
Figured it out, wasn’t paying attention to the two migrations you had setup, only did the first one.
Thanks,
Matthew
I did a post on Devise as well and it’s gotten a ton of views. Yours is way more detailed…I’ll have to start sending folks here! Great post. Thanks.
Hi, nice article!
I just want you to know, that I spent months creating a project Cream which attempts to make it dead simple to setup a complete Authentication, Authorization with Roles solution, using Devise and CanCan for your ORM of choice. I can recognize much of what I did in this article
Check out Cream on github! Cheers!
Kristian
Thanks for the great post. One thing though…I found the code for role? a little confusing
return !!self.roles.find_by_name(role.to_s)So I use this instead
self.roles.exists?(:name => role.to_s)This is a minor question. Why do you use !! in
!!self.roles.find_by_name(role.to_s.camelize)
Does it do anything? Just trying to understand the code.
this method don’t work with postgres:
def role?(role)
return !!self.roles.find_by_name(role.to_s.camelize)
end
@mrharrison – You are missing a table. Maybe you forgot to run the migrations
@Dan – Looks better!
@Julia – Double bang !! will make that statement return false (boolean) if the role is not found
@ Maciej – I don’t use Postgres but since everything is going through AR then it might be an AR bug? I suggest looking at the generated query and figuring out why it doesn’t work
Tony, great tutorial, but I have the same problem like Gave.
I can’t create admin acc through console because I can’t generate encrypted password and salt, token,…
Can you help me with this?
In general, it would be helpful if you specify where things go.
For example you talk about the role method, but don’t specify where we should add this method. I know, most are obvious, but having a small text on top of the code sample specifying which file you are editing would help.
Just something that I think would make the tutorial more clear for new users.
Thanks again for your effort
Awesom stuff. Detailed and very helpful. One question though…
Although I understand how !! ( not not ) works I cant see how it would ever return false in the above code when a role DOES NOT exist.
If you try to find a record using find_by_some_attribute and it doesnt exist then an empty array is returned and if you use !! on an empty array you gert
!![] ==> true
but in this context you actually want false.
or am i missing something here?
It is great tutorial. I suggest that you at least mention: add line like gem ‘devise’, ‘1.2.1′ to your Gemfile.
7 Trackbacks
[...] Administrators By Tony | Published: September 29, 2010 About two months ago I wrote an article on getting started with Devise and CanCan. Since then, I’ve implemented the Devise + CanCan combo on three projects and wrote a couple [...]
[...] frameworks, doesn’t have anywhere near the number of pre-written plugins to solve issues like adding roles and permissions to your site or put together an e-commerce [...]
[...] these great articles on another blog that describes using Rails and CanCan together: Article 1: Getting Started with Devise and CanCan The article includes very detailed steps and code samples for both Devise and CanCan. Article [...]
[...] there any database-managed roll based access control Gems on Rails 3.0?I found devise + CanCan: http://www.tonyamoyal.com/2010/0… .But its roll is hard-coded (ex. Ability class).I want to use roll based permission control gem [...]
[...] from Devise’s official github page, Asciicast’s episode on CanCan by Ryan Bates, and Tony Amoyal’s tutorial. I took most of the codes from these resources and then slightly modified it to meet my own needs. [...]
[...] Starting to fall for Rails A combination of Devise and Cancan might be useful, as shown herehttp://www.tonyamoyal.com/2010/0…11:35amView All 0 CommentsCannot add comment at this time. Add [...]
[...] $ rails generate devise:views 10. Formate os templates das views em app/views/devise Fonte: http://www.tonyamoyal.com/2010/07/28/rails-authentication-with-devise-and-cancan-customizing-devise-... [...]