Devise
Authentication Made Easier
With most web applications, users will have the option to sign in or create a new account. Each time someone does this, we as developers are responsible for ensuring a safe browsing experience. We can do this by identifying, authenticating, authorizing, then allowing access to our users according to their access policy. But a good programmer is efficient. So what’s the easiest way to do this?
Cue Devise. Devise is a Ruby gem based on Warden that simplifies the user authentication process by tenfold. To get a good understanding of this, let’s go through the steps of manual authentication.
Manual Authentication
- Generate your MVC framework.
$ rails g model user username:string email :string password_digest:string$ rails g controller users$ rails g controller sessions$ rake db:migrate
The first line will create your User
model and add username
, email
, and password_digest
attributes. password_digest
comes from the macro has_secure_password
because we never want to store passwords in plain text.
The second and third lines generate your Users
and Sessions
controllers, respectively.
The fourth line creates and updates your User
table.
2. Add validations and has_secure_password to the User
model.
class User < ActiveRecord::Base has_secure_password validates :name, :password, presence: true validates :email, presence: true, uniqueness: trueend
This will make sure users are not able to create an account if they are missing an attribute. has_secure_password
is a macro that comes from ActiveRecord::Base
. It works with BCrypt to salt and hash user passwords and is what allows us to add validations.
3. Create controller actions for user signup.
First we’ll need a placeholder for a new user:
def new @user = User.newend
And then a POST request:
def create @user = User.create(user_params) if @user.save session[:user_id] = @user.id show_welcome_page else render :new endend
The new @user
will be created from the parameters received in the views. If @user
passes validations passed in the model, a session[:user_id]
key will be created from the @user
's id. Otherwise, the user will be prompted to try again.
4. Create a controller action for user login.
def create @user = User.find_by(email: params[:user][:email]) if @user && @user.authenticate(params[:user][:password]) session[:user_id] = @user.id show_welcome_page else @errors = ["Please enter a valid email and password."] render :new endend
First, we are finding the user by the email they are entering into the form provided in views. If the user exists and the user is authenticated, a session is created. Else, they will be prompted to try again.
This is similar to our signup action, but has some unfamiliar code:
@user.authenticate(params[:user][:password])
But no worries! All this is doing is receiving the user’s password input, salting and hashing this input, and comparing it to the string that has already been salted and hashed in the database. This is possible through has_secure_password
, which provides us with the #authenticate
method.
So why choose Devise?
It’s Widely Used
Devise is widely used, meaning it’s trusted! It’s likely that the gem is well maintained and regularly scanned for bugs and improvements!
It Has Helper Methods
Devise comes with its own ready-to-use helper methods, giving us a lot less work! These methods includeuser_signed_in?
, current_user
, user_session
, and even log in, log out, and sign up methods.
It Cleans Our Code
As with all great gems, Devise keeps our code DRY, increasing readability.
It Has Many Options
It’s composed of 10 modules, giving users and developers plenty of options. They include:
Trackable: tracks sign in count, timestamps and IP address.
Timeoutable: expires sessions that have not been active in a specified period of time.
Lockable: locks an account after a specified number of failed sign-in attempts. Can unlock via email or after a specified time period.
These modules are extremely useful in detecting fraudulent activity and preventing malicious hackers from getting into your account and precious information. Don’t believe me? See for yourself! Let’s take a look at some of the source code..
Was there a sign-in to your account with an IP address thousands and thousands of miles away? With Devise, you can keep track of this information and take immediate action in order to secure your privacy.
def extract_ip_from(request)
request.remote_ip
end
Did you leave your laptop at work and forget to logout of your bank account? No worries. Devise will ask you to log in again after your session has timed out.
def timedout?(last_access)
!timeout_in.nil? && last_access && last_access <= timeout_in.ago
end
Someone’s trying to guess your password? That’s not cool. Devise will check for the number of attempts made to access your account and will squash any further movement for a period of time.
def attempts_exceeded?
self.failed_attempts >= self.class.maximum_attempts
end
And if you are trying to access your account earlier than that, no worries! Devise also sends you an email…
def send_unlock_instructions
raw, enc = Devise.token_generator.generate(self.class, :unlock_token)
self.unlock_token = enc
save(validate: false)
send_devise_notification(:unlock_instructions, raw, {})
raw
end
… to unlock your account!
def unlock_access!
self.locked_at = nil
self.failed_attempts = 0 if respond_to?(:failed_attempts=)
self.unlock_token = nil if respond_to?(:unlock_token=)
save(validate: false)
end
Imagine how many more useful methods we have access to with the complete list! How much work and typing are we saved from by installing this gem? You can see a complete list of modules at the Devise documentation.
Installation
Now that we know why we should use Devise, how can we started?
- In your Gemfile, add
gem ‘devise’
, then run$ bundle install
in your console. - Still in your console, run the generator:
$ rails generate devise:install
3. You will see instructions appear on the console. Continue by reading through these and following along with each step! By the end, you should have your default URL for the Devise mailer set up.
Check out this Devise cheatsheet and the video below for further guidance.
Did you know?
Both HTTP and our rails applications are stateless. This means that each time we are creating a new request, we are relying on cookies and sessions. Cookies are hashes that store information, such as user information, in the browser. Sessions, though not true hashes, behave like hashes. They are similar to cookies, but will store the information received from cookies on the server side.