Rails: User/Password Authentication from Scratch, Part I

(Note: Source code accompanying this article can be found here.)

Today we’re going to learn how to implement simple user authentication in a Rails application from scratch. We’ll examine best practices to help avoid common, often costly, mistakes.


Introduction to User Authentication

Password-protected actions are a common feature in most web applications, only allowing registered users in with a valid password. This is called “User Authentication”, and many Rails applications need it. Let’s start with a quick scenario of how the user authentication process works.

  • Signup: create a new user. This user is going to register with a username, password (which will be encrypted in the database), email, etc.
  • Login: allow a user to sign in with her/his valid username and password. The authentication process happens by matching the username and password in the database, allowing the user access to the protected actions only if the given information matches the recorded values successfully. If not, the user will be redirected to the login page again.
  • Access Restriction: create a session to hold the authenticated user ID after login, so navigation through additional protected actions can be done easily by just checking the userID in the current session.
  • Logout: allow the user to sign out and set the authenticated userID in session file to nil.

Generate User Model & Controller

First, let’s create our application named User_Auth. We’re using mysql as our database:

Navigate to the application directory in your terminal and generate the User Model and Controller. The controller will get a new method as a parameter.


Setup the Database

Now we need to create a Users table in the database. As you probably know, that is just a simple migration away.

We’ve added 4 columns in the Users table (username, email, encrypted_password & salt) Remember that we never store passwords in plain text, always encrypting the value first before saving it to the database. There are different types of encryption techniques, which we’re are going to look at later in this tutorial.

“We never store passwords in plain text, it should always be encrypted first before saving it to the database.”

After we have created the model and migration successfully, create the database and migrate it to create the Users table.


Creating the New User Action

Next, let’s write the new and create actions in the UsersController.

We’ve created two main actions:

  • newsimply create a new user object to be rendered in the new template (we’ll see it soon ). The template includes a sign up form. The data in this form will be sent to the create action, described next…
  • create: creates the user based on the parameters passed from the new template and saves it to the database. If the user is created successfully, redirect the user to wherever you choose after the signup action. Here we place “You signed up successfully” in the flash hash to indicate success, otherwise render the new template again.

Sign-up Form template

Before writing the signup form, I created a simple layout in the application.html.erb file inside the views/layout directory.

Now, let’s write signup form inside the new template.

Here, we’ve created a form for signup which takes a username, email, password and confirmation password from the user. These values will be sent it as params[:user] to the create action. The  if statement checks for errors in case the user enters invalid data.

You also might have noticed that the signup form has two fields: password and password_confirmation that must match a user in database. We should add attr_accessor methods in the user model to handle these.


Adding Some Validations to the User Model

In addition to attr_accessors, we need to add some validation rules to make sure that the input data fits our requirements.

We’ve created a signup page that creates a new user and validates the input data, but we didn’t encrypte the password. Before we do that, let’s talk about password encryption.


Password Encryption Techniques

As previously mentioned, never store passwords in the database as plain text. Encrypting passwords is a best practice and fundamental to all user authentication approaches in Rails.

Hashing Password

Hashing is the process of applying mathematical functions and algorithms to a string of data to produce a unique output string. While creating a new user, the plain text password gets hashed then saved into the database. When the user signs in, the input password gets hashed and compared with the hashed password stored in the database. This technique is called one-way encryption, meaning, the same inputs with the same hashing function will always give the same output.

We can implement a hashed method using SHA1 in Rails with just two lines of code

Salting Password

Due to some drawbacks that exist in the hashing password technique, like Rainbow tables, salting a password is a more secure way to encrypt passwords. ‘Salt’ is an additional string of data added to the password before encrypting it. It should be unique and random, to render the Rainbow tables flaw useless.

Encryption using salt:

Bcrypt

There’s another easy way to encrypt passwords rather than making a salt from scratch: use bcrypt. bcrypt-ruby is a ruby gem for encryption and we’ll use it in our application.

To install it, just add the following into gem file:

and inside your application directory run:

then you can simply write:


Callbacks

We need two functions: one to encrypt the actual password (plain text) before saving the user record and the other function to assign the password attr_accessor to nil. As we have already encrypted and stored password in the database, we will not use it anymore, and we can do this using the before_save and after_save callbacks.

Now, let’s add these functions and callbacks to the user model


Mass Assignment Protection

One of the most common security issues in Rails is called the “mass assignment vulnerability”, and it stems from the ActiveRecord convention of creating getters and setters for all the attribute values of an ActiveRecord object.

In the create action, rather than directly assign each attribute one by one, we use a hash of all the values that we want to assign to the attributes of subject. This is the aforementioned mass assignment and is the crux of the issue. There are attributes we do not want the user to be able to change via the form, but we aren’t checking the parameters.

To avoid it, there’s two methods in Rails to protect attributes from mass assignment

  • attr_protected: all attributes marked with attr_protected are ignored during mass assignment and all other attributes will be accessible.
  • attr_accessible: all attributes marekd with attr_accessible are accessible during mass assignment and all other attributes will be protected.

Finally let’s add the accessible attributes into user model

This completes signup process! You can run your server, sign up as a new user and test it to ensure that password is encrypted in the database successfully!

Note: Don’t forget to add a default route in the routes file! You can just un-comment at in the end of file.


Still More to Do

There is still more work before we can say the user authentication process is complete. In my next post, I’ll cover working with sessions and cookies, access restriction, and route configuration. Thanks for reading!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Lee Smith

    Nice work! I did something very similar a while back (Rails 3.1).

    https://github.com/leesmith/decent_authentication

  • http://blog.solid1pxred.com Tieg Zaharia

    There’s also a new BCrypt-based password helper in 3.2+ called “has_secure_password” that will take care of some of this:

    https://github.com/rails/rails/blob/826a85069627060c11baf932423702b1228dd4df/activemodel/lib/active_model/secure_password.rb#L34

  • Luca

    Hi,
    great tutorial, but I can’t find the application layout gist.

  • Luca

    Sorry for the message, I find the source on github
    Thanks again for the tutorial

  • Paul Spain

    Thanks Karim! Clear and concise. I appreciated the link to Rainbow Tables too. Looking forward to the next post

  • Lee Smith

    Built-in password encryption was introduced in Rails 3.1. You should definitely take advantage of it to reduce code complexity.

    http://bcardarella.com/post/4668842452/exploring-rails-3-1-activemodel-securepassword

  • Hadeer Gamal

    Great article. Thanks a lot..

  • Ricky

    Error in:
    {:controller => ‘users’, :action => create}) do |f| %>
    May be:
    {:controller => ‘users’, :action => ‘create’}) do |f| %>

  • Pavel

    Is it really possible for attacker to have your password database and not to have salt?

    Also, with GPU computing Rainbow tables are obsolete.

  • http://rohitsharma9889.wordpress.com Rohit Sharma

    I think the EMAIL_REGEX is a bit buggy
    I used the email address test@test and it worked.

  • RubyOnRails

    for email validation use this regular expression

    EMAIL_REGEX = /A[w+-.]+@[a-zd-.]+.[a-z]+z/i

  • scarlet88

    it works fine…thanks a lot

  • pwz2k

    Which directory are you suppose to create the password_hashing.rb file in? I can’t find it in the Github and the tutorial does not explain where to create it at.