Setting up Bcrypt in your Rails project
Tags: beginners, ruby, rails, security
Published: 2023-08-23 22:08:35

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:

```sh
rails new ProjectName
```

- Add the 'bcrypt' Gem to your project's `Gemfile`:

```sh
gem 'bcrypt', '~> 3.1.13'
``` 

In your terminal, run:

```sh
bundle install
```

- 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. 

```sh
rails g model User name email password_digest
```

- In the model, add your associations and validations and include `has_secure_password`

```ruby
class User < ApplicationRecord
    has_secure_password

    # Include additional validations...
end
```

- Migrate your database to create your `users` table 

```sh
rails db:migrate
```

- 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`. 

```ruby
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
```

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`. 

```ruby
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
```
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. 

```sh
$2b$10$nOUIs5kJ7naTuTFkBy1veuK0kSxUFXfuaOKdOKf9xYT0KKIGSJwFa
 |  |  |                     |
 |  |  |                     hash-value = K0kSxUFXfuaOKdOKf9xYT0KKIGSJwFa
 |  |  |
 |  |  salt = nOUIs5kJ7naTuTFkBy1veu
 |  |
 |  cost-factor => 10 = 2^10 rounds
 |
 hash-algorithm identifier => 2b = BCrypt
```
Read more about Bcrypt for Ruby [here.](a://github.com/bcrypt-ruby/bcrypt-ruby)