DEV Community

Mandy Petrakis
Mandy Petrakis

Posted on • Edited on

Setting up Bcrypt in your Rails project

If you're here you probably already know that storing passwords in plain text is a bad idea. Here is a step-by-step guide on how to set up Bcrypt in your Rails project. It's quite simple, yet offers powerful security to your user's information as well as any sensitive data you might be storing in your database. It's so simple in fact, you'll probably have time to read about how it works at the end if you aren't already familiar.

  • If you don't have your Rails project set up go ahead a run:
rails new ProjectName
Enter fullscreen mode Exit fullscreen mode
  • Add the 'bcrypt' Gem to your project's Gemfile:
gem 'bcrypt', '~> 3.1.13'
Enter fullscreen mode Exit fullscreen mode

In your terminal, run:

bundle install
Enter fullscreen mode Exit fullscreen mode
  • Generate the model you will be storing the password in and include password_digest as an attribute. Remember the default type when using a generator is a string which is how the password_digest will be stored.
rails g model User name email password_digest
Enter fullscreen mode Exit fullscreen mode
  • In the model, add your associations and validations and include has_secure_password
class User < ApplicationRecord
    has_secure_password

    # Include additional validations...
end
Enter fullscreen mode Exit fullscreen mode
  • Migrate your database to create your users table
rails db:migrate
Enter fullscreen mode Exit fullscreen mode
  • Implement user registration in your users_controller with a create action along with your error handling. Though we included a password_digest attribute in our user table, we will still take in a password and password_confirmation in our user_params. Bcrypt will do the work of hashing the plain text password and storing it as password_digest thanks to that handy single line of code in step 4, has_secure_password.
class UsersController < ApplicationController 

rescue_from ActiveRecord::RecordInvalid, with: :render_record_invalid

     def create 
          user = User.create!(user_params) 
      session[:user_id] = user.id
        # Include any additional successful registration steps
    end

private 

     def user_params 
          params.permit(:name, :email, :password, :password_confirmation) 
     end 

     def render_record_invalid(e)
          render json: {errors: e.record.errors.full_messages}, status: :unprocessable_entity
     end
end
Enter fullscreen mode Exit fullscreen mode

So far the code above will take in new user information as use_params and create a new user. It will verify that the password and password_digest match and save the new user to the database if so along with the Bcrypt generated password_digest. If the password and confirmation do not match, the bang operator in User.create! will raise an error that will be rescued with the render_record_invalid function to return any errors so we can render them to the dom for our user.

  • Lastly, we will authenticate our users when they sign in. This will go into the cont roller responsible for your user authentication, such as a sessions_controller. First, the action will find the user via their email, in this example and authenticate the password they input against the store password_digest.
class SessionsController < ApplicationController 
     def create 
    user = User.find_by(email: params[:email]) 
    if user && user.authenticate(params[:password]) 
        # Handle successful login 
    else 
        # Handle login failure 
     end 
  end
end
Enter fullscreen mode Exit fullscreen mode

Thats it! Your passwords are being stored securely.

How it works:

Bcrypt is a hashing algorithm that takes a bit of data (e.g. password) and creates a "digital fingerprint" from it. In other words 'password' becomes something like 2b$10$nOUIs5kJ7naTuTFkBy1veuK0kSxUFXfuaOKdOKf9xYT0KKIGSJwFa.

What makes Bcrypt more secure is that it adds "salt" to the password before hashing it. Salt is a random bit of additional data that is prepended to the password the user entered before it is hashed. This looks like turning our password into E4OAovh7rbpassword before processing it through the hashing algorithm. This way, if two people have have the same password, the salt ensures that their password_digest is completely different.

So how do we authenticate the user?

Bcrypt stores the password salt prepended on the password hash in the password_digest so all we have to do is add that salt to the password the user entered upon log in and see if it matches. Yes, the stored hash does have embedded information about its computation. As seen in the example below from the Bcrypt official npm page.

$2b$10$nOUIs5kJ7naTuTFkBy1veuK0kSxUFXfuaOKdOKf9xYT0KKIGSJwFa
 |  |  |                     |
 |  |  |                     hash-value = K0kSxUFXfuaOKdOKf9xYT0KKIGSJwFa
 |  |  |
 |  |  salt = nOUIs5kJ7naTuTFkBy1veu
 |  |
 |  cost-factor => 10 = 2^10 rounds
 |
 hash-algorithm identifier => 2b = BCrypt
Enter fullscreen mode Exit fullscreen mode

Read more about Bcrypt for Ruby here.

Top comments (3)

Collapse
 
cherryramatis profile image
Cherry Ramatis

Awesome beginner article! If it’s cool I would like to provide some tips to improve the overall readability of your post, but the content itself is awesome congratulation!

  1. Add syntax highlight to your code blocks by adding “ruby” after the three backticks
  2. For the commands like “rails db:migrate”, add to a code block as well with sh for the language, that way it’s easier to read and copy
  3. Fix your numeric list, currently all your item list use 1., it would be better to continue increasing the number on the subsequent items.

Hope this is useful and congrats on your article again!

Collapse
 
mandy_petrakis profile image
Mandy Petrakis

Thanks so much! Very helpful.

Collapse
 
cherryramatis profile image
Cherry Ramatis

It's always a pleasure!