Devise Authentication in Rails 3 (Updated for devise 2)
Devise Github project page: https://github.com/plataformatec/devise
After some google searching I arrived at the decision to use Devise over Authlogic for my brand spanking new rails 3 app. I haven’t really had that much experience with authlogic in the past, besides using it in my previous rails 3 app. It was a little tricky to get it working with rails 3 and I wasn’t the biggest fan of the documentation (I felt it could have been a little more granular). That being said I have never used Devise but the yammering on the internet suggests it is a cleaner solution and works out of the box with rails 3. (http://stackoverflow.com/questions/4136121/rails-3-authentication-authlogic-vs-devise) I also liked some of the out of the box features it came with:
- 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 a 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.
- 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.
- Encryptable: adds support of other authentication mechanisms besides the built-in Bcrypt (the default).
The Exploration
First things first, add “devise” to your rails Gemfile in the root directory of your application.
Note: this example assumes knowledge of the bundler gem
1 |
bundle install |
Running bundle install will set you up with:
1 2 3 |
Installing bcrypt-ruby (3.0.1) with native extensions
Installing warden (1.1.1)
Installing devise (2.0.4) |
Following the install docs on the Devise project page..
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
rails generate devise:install
create config/initializers/devise.rb
create config/locales/devise.en.yml
===============================================================================
Some setup you must do manually if you haven't yet:
1. Setup default url options for your specific environment. Here is an
example of development environment:
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
This is a required Rails configuration. In production it must be the
actual host of your application
2. Ensure you have defined root_url to *something* in your config/routes.rb.
For example:
root :to => "home#index"
3. Ensure you have flash messages in app/views/layouts/application.html.erb.
For example:
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
=============================================================================== |
Alright, based on the install output it looks like we need to do a little setup work… However, I will leave that to the reader since they may be using a pre-existing rails application…
After you are finished configuring the application its time to create your user model.
1 2 3 4 5 6 7 8 9 |
rails generate devise User
invoke active_record
create app/models/user.rb
invoke test_unit
create test/unit/user_test.rb
create test/fixtures/users.yml
create db/migrate/20110108192129_devise_create_users.rb
insert app/models/user.rb
route devise_for :users |
Great, lets take a look at what the generator created starting with the migration.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
## Database authenticatable
t.string :email, :null => false, :default => ""
t.string :encrypted_password, :null => false, :default => ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, :default => 0
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## Encryptable
# t.string :password_salt
## Confirmable
t.string :confirmation_token
t.datetime :confirmed_at
t.datetime :confirmation_sent_at
t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts
t.string :unlock_token # Only if unlock strategy is :email or :both
t.datetime :locked_at
## Token authenticatable
# t.string :authentication_token
t.timestamps
end
add_index :users, :email, :unique => true
add_index :users, :reset_password_token, :unique => true
# add_index :users, :confirmation_token, :unique => true
# add_index :users, :unlock_token, :unique => true
# add_index :users, :authentication_token, :unique => true
end
end |
It looks like Devise gives you password encryption/storing, password recovery, session tokens, and login attempts by default. For my application I will also uncomment the lines for Confirmable, Lockable and Token authenticatable since I find these registration options useful out of the box and will save me time from writing those components later.
The only thing I needed to investigate of these options was the Lockable option since I wanted to understand the lock and unlock strategies.
1 2 |
:unlock_strategy - The strategy used for unlock. Can be :time, :email, :both (default), :none.
If :email or :both, creates a unlock_token field. |
1 |
:lock_strategy - The strategy used for locking. Can be :failed_attempts (default) or :none. |
Alright, now that we understand what they mean here is what my final migration looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
## Database authenticatable
t.string :email, :null => false, :default => ""
t.string :encrypted_password, :null => false, :default => ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, :default => 0
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## Encryptable
# t.string :password_salt
## Confirmable
t.string :confirmation_token
t.datetime :confirmed_at
t.datetime :confirmation_sent_at
t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts
t.string :unlock_token # Only if unlock strategy is :email or :both
t.datetime :locked_at
## Token authenticatable
t.string :authentication_token
t.timestamps
end
add_index :users, :email, :unique => true
add_index :users, :reset_password_token, :unique => true
add_index :users, :confirmation_token, :unique => true
add_index :users, :unlock_token, :unique => true
add_index :users, :authentication_token, :unique => true
end
end |
Now let’s take a look at our User model.
1 2 3 4 5 6 7 8 |
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable, :lockable and :timeoutable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
end |
It looks like based on our migration options that we will also have to uncomment token_authenticatable, :confirmable, :lockable and :timeoutable and add it to the devise option.
1 2 3 4 5 6 7 8 9 |
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:token_authenticatable, :confirmable, :lockable, :timeoutable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
end |
We don’t need to setup our routes for user because devise has already added devise_for :users to our config/routes.rb for us. If we then run rake routes from the command line we get:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
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"}
user_registration POST /users(.:format) {:action=>"create", :controller=>"devise/registrations"}
new_user_registration GET /users/sign_up(.:format) {:action=>"new", :controller=>"devise/registrations"}
edit_user_registration GET /users/edit(.:format) {:action=>"edit", :controller=>"devise/registrations"}
user_registration PUT /users(.:format) {:action=>"update", :controller=>"devise/registrations"}
user_registration DELETE /users(.:format) {:action=>"destroy", :controller=>"devise/registrations"}
user_confirmation POST /users/confirmation(.:format) {:action=>"create", :controller=>"devise/confirmations"}
new_user_confirmation GET /users/confirmation/new(.:format) {:action=>"new", :controller=>"devise/confirmations"}
user_confirmation GET /users/confirmation(.:format) {:action=>"show", :controller=>"devise/confirmations"}
user_unlock POST /users/unlock(.:format) {:action=>"create", :controller=>"devise/unlocks"}
new_user_unlock GET /users/unlock/new(.:format) {:action=>"new", :controller=>"devise/unlocks"}
user_unlock GET /users/unlock(.:format) {:action=>"show", :controller=>"devise/unlocks"}
root /(.:format) {:controller=>"home", :action=>"index"} |
Finally it’s time to migrate our database and create our user model.
1 |
rake db:migrate |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
== DeviseCreateUsers: migrating ==============================================
-- create_table(:users)
NOTICE: CREATE TABLE will create implicit sequence "users_id_seq" for serial column "users.id"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "users_pkey" for table "users"
-> 0.0553s
-- add_index(:users, :email, {:unique=>true})
-> 0.0020s
-- add_index(:users, :reset_password_token, {:unique=>true})
-> 0.0021s
-- add_index(:users, :confirmation_token, {:unique=>true})
-> 0.0115s
-- add_index(:users, :unlock_token, {:unique=>true})
-> 0.0028s
== DeviseCreateUsers: migrated (0.0740s) ===================================== |
Now it’s time to start our rails server and see what devise has given us. Run rails server from the command line and lets travel to “localhost:3000/users/sign_up” and see whats going on.
My first thoughts are “Wow, that’s kind of ugly”. haha. Looks like down the line I will have to override those views and build custom ones (by overriding the routes!). I then attempt to sign up by filling out the form. The page reloads and tells me “You have signed up successfully. If enabled, a confirmation was sent to your e-mail.”. Yay, alright now I realize immediatly that I haven’t yet setup an smtp server on my local machine however if we check the server output or logs we will see a generated message:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Sent mail to lee@blazingcloud.net (58ms)
Date: Sat, 08 Jan 2011 12:04:04 -0800
From: please-change-me@config-initializers-devise.com
To: lee@blazingcloud.net
Message-ID: <4d28c334b84a5_7cb680e634b492756@Atum.local.mail>
Subject: Confirmation instructions
Mime-Version: 1.0
Content-Type: text/html;
charset=UTF-8
Content-Transfer-Encoding: 7bit
<p>Welcome lee@blazingcloud.net!</p>
<p>You can confirm your account through the link below:</p>
<p><a href="http://localhost:3000/users/confirmation?confirmation_token=wYQlv1D1lbB9InFq8dVZ">Confirm my account</a></p>
Sent mail to lee@blazingcloud.net (58ms)Date: Sat, 08 Jan 2011 12:04:04 -0800From: please-change-me@config-initializers-devise.comTo: lee@blazingcloud.netMessage-ID: <4d28c334b84a5_7cb680e634b492756@Atum.local.mail>Subject: Confirmation instructionsMime-Version: 1.0Content-Type: text/html; charset=UTF-8Content-Transfer-Encoding: 7bit
<p>Welcome lee@blazingcloud.net!</p>
<p>You can confirm your account through the link below:</p>
<p><a href="http://localhost:3000/users/confirmation?confirmation_token=wYQlv1D1lbB9InFq8dVZ">Confirm my account</a></p> |
Fantastic if we go to the confirmation address http://localhost:3000/users/confirmation?confirmation_token=wYQlv1D1lbB9InFq8dVZ we should be able to active our new account and sure enough the confirm works and sends us to the homepage with the message ‘Your account was successfully confirmed. You are now signed in.’
Conclusion
Devise is easy to setup and install using Rails 3. It give me the flexibility and features I need to fulfill the authentication requirements of my application. Good documentation and setup instructions go a long way. We will see with time if I regret my decision. If anyone out there in the internets know of any helpful information that would be beneficial to this blog post please comment below!
Update
To update your Devise views run this from the command line:
1 |
rails generate devise:views |
This will pull the views from the gem and allow you to modify them.
13 Comments
Thank you for the excellent walk-through. I followed this nearly verbatim, and it worked in my development environment. The only thing I’d recommend for future readers who may be relatively new to rails is that after running bundle install, the first instruction: ” 1. Setup default url options for your specific environment . . . .” means to add the example line (or another similar line) to config/environments/development.rb (or another environment).
Very useful post for this newbie. I had gotten devise working fine in production which sent a confirmation email and was as happy as a clam. I only realized I had a problem when my local development site locked me out since I hadn’t confirmed. Your post came to the rescue. Thanks!
I went from deciding I was too stupid to get devise working to…having devise working. Thanks!
Hi,
Clear explanation, Great post.
I had to add this line to config/initializers/devise.rb
require ‘devise/orm/active_record’
Hi,
First of all thanks a lot for this tutorial. I am new to rails and this helped me a lot. Upto signup, it worked perfectly fine, but after i restarted the server, when i try to access sign_up page, it redirects me to localhost:3000. Please help me to correct this. Below is what i see in server log:
Started GET “/users/sign_in” for 127.0.0.1 at Sun May 04 00:58:24 +0530 2008
Processing by Devise::SessionsController#new as HTML
←[1m←[36mUser Load (0.0ms)←[0m ←[1mSELECT
users
.* FROMusers
WHEREusers
.id
= 1 LIMIT 1←[0mRedirected to http://localhost:3000/
Completed 302 Found in 109ms
I followed all the steps. Sign up process is woking fine. Everything is getting stored in the database. But sign_in process is not working for valid users also. It is giving invalid email or password. Please help me…
I am new to rails and its helped me a lot. Anybody can tell, how to make profile with this login for particular user and display like as in facebook or any social networking.
I am new to rails and this code helped me a lot. I am working on project for my college to create profile for students.
Is there any tutorial in RUBY to create small application like similar to facebook. Just need to display their profile, and can send messages.
I haven’t read it, but the book RailsSpace covers the implementation of a social network. It’s by Michael Hartl and I like other stuff I’ve read of his.
Greeting, I’m also new on rails. And this post was helping me to understand about how to getting started on devise, although there is several bug on my devise trial on how that the email confirmation still error, and sign in won’t work because there is no email confirmation for those user. But I rather ask to o how actually that Devise work to save session and control session on my whole page, I still confuse on that things..
Any answer would be appreciated.
Regard,
Kristono Sugiarto
nice explanation…. it would be great could you please share an example which shows SSO using devise authentication and OmniAuth
Thanks so much. This is very very helpful
3 Trackbacks
[...] This post was mentioned on Twitter by Blazing Cloud, Inc., Lee Lundrigan. Lee Lundrigan said: Devise for Rails 3 tutorial: http://t.co/TPecd1S [...]
[...] [...]
[...] 2A really good guide to integrating Devise with your Rails app [...]