BCrypt and User Authentication

Peregrin Garet
6 min readOct 20, 2016

As a class at Flatiron, we’ve been working pretty heavily with Sinatra and ActiveRecord the past couple days — Sinatra handles the website routing, ActiveRecord manages the database.

However, one limiting factor is that there is only one admin user who can see all the data, perform all the actions, go to all the pages, etc. Most websites have user accounts that determine the potential user experiences. Thus, I decided to set out and create an app that has multiple users, with all that entails.

Generic Login Screen #52

To clarify, when I think of a user experience complete with logins, I assume the user must enter a login name and password which must be a valid pair.

Once logged in, the user will only see certain content and be able to perform certain actions.

The user will be unable to access the content that would have been visible had they logged in as a different user.

For this project, I modeled a very primitive shop app. Visible is the schema from my database.

As a quick reference — there are Users and Items, each User has a Cart and Items can belong to a Cart.

Through ActiveRecords’ relationship modeling and foreign keys, we can get a Cart’s User and Items, a User’s Cart and Items, and an Item’s Cart and User.

It seems appropriate at this stage to pause to recognize BCrypt, the password encryption tool (and Ruby gem) that I used for this project. BCrypt is the current industry standard for data encryption. It follows the following procedure:

1. Take a chunk of data (i.e. a password)
2. Generate a completely random chunk of data called a salt
3. Append the salt to the data (the password)
4. Generate the unique digital footprint for that combined salt+data, called a hash
5. BCrypt generates and stores the salt and hash
6. We get a value that we want to compare to that original data
7. BCrypt brings out the relevant salt, appends it to the new value, and converts that to hash form
8. If the stored hash and the new hash are equivalent, the new value is equal to the original data

That was probably the most technical part of this whole thing, so if you’re still following along, nicely done. I’m including the link to the Github page for BCrypt here, so that you can check out the author’s explanation. I’m also demonstrating below — I create a User called example, give example a password, and show the hash that the password becomes. I then test the hashed password against the original password value, and it comes out true.

This is where most of that particular magic happens — the User model. As can be seen, we have Getter and Setter methods for the password instance variable.

Note we’re including BCrypt, and Password is a class provided by the BCrypt gem.

The Getter password method checks to see whether the password exists in this instance of User. If not, it creates a new Password hash based on the password_hash element of the User in the database.

The setter method includes the argument for what the actual password should be, then creates it by passing that argument and generating a hash, which is then assigned as the value for the instance variable password.

Let’s quickly summarize the current status of our User Authentication system: Users can be assigned passwords, which are promptly encrypted. Those passwords when called for, return the encrypted hash.

Now, of course, comes other half of the project — actually setting up a web app that utilizes that lovely new functionality.

Here are the methods I implemented at the User level, for various functions in the app.

Creating an account sets up the password. It also creates and attaches a Cart for the user.

Log_out and Log_in, predictably, determine whether the user is logged in. If the user logs out, the Empty user is logged in. The Empty user is used is in a series of if statements around the app in places where functionality or UI is different. Imagine if you will that Empty user is a Read-Only user type, whereas a logged in user can Write as well. To tie in my previous blog on Static vs Dynamic sites, for the Empty user the site is Static, whereas the logged in user can make changes to the database.

Here is where we actually login. This is in the Application Controller. We’re expecting a get request and a post request to ‘/login/’. The get function returns the login screen.

The post function finds or creates the user based on their name, and is where everything really starts tying together.

Here we first find or create the user by name (usernames for this app must be unique). We then check if the user has a password (aka are they new), and if so we call the create_account function we made earlier.

Otherwise, we check if the passwords match (BCrypt keeps the password safe while being checked). If they do, we use log_in. Otherwise, we load up the incorrect login page, and have the user try again.

Within the UsersController, we also have some get requests prepared for that require checking checking logins. After all, users can’t add anything to their cart if they aren’t logged in, and sometimes user accounts will see different things about items.

That’s a pretty solid start — we now have a login screen that allows users to log in or create a new account, and when users try to log in with incorrect data, it doesn’t let them get in. Let’s take actually run through the process with our old friend example.

Step 1) Create a user with username ‘example’ and password ‘my password’

Alright! We’re now logged in as Example, and returned to the index page of Items available to be added into the cart.

But we want to log out, lest the next user of the app buy something in our name!

The result of logging out is quite identical to the original login page.

Some sneaky person noticed that our extremely clever username is example, and decided to try and login on our behalf for whatever nefarious purpose.

Not so fast! That password is not correct, so we’re going to throw an error.

We come back to find to the evidence of this attempted moral atrocity, but fortunately we do in fact remember our own password.

And just like that, we’re back in.

So in a nutshell —

  1. Users choose passwords which are turned into encrypted hashes
  2. Users log in with their chosen password which is compared against the hash
  3. If the password is valid, they are logged in. Otherwise, they get an error message

And with that, today’s lesson is complete.

BCrypt’s Github link again — https://github.com/codahale/bcrypt-ruby

--

--