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:
- new: simply 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 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
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:
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:
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!