Rails Token Authentication with JWT and BCrypt Gems

Gary Cordero Rosa
4 min readMar 8, 2021

One of the most popular ways of authentication use in rails 5 and higher is the token-based authentication, where a generated created when an user logs in and such token is pass with every request the user make to as a proof of identity to authorize access to resources in the server. In this guide I am going to show you how to quickly set up a token-base authentication API in Rails 6 using JWT and BCrypt gems.

Lets start by creating a new project

rails new api-app --api

Them let’s modify the cors.rb file so it can take cors.rb to allow Cross origin resource sharing. More info about rack-cors in ruby https://github.com/cyu/rack-cors

//config/initializers/cors.rb
# Be sure to restart your server when you modify this file.
# Avoid CORS issues when API is called from the frontend app.
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.# Read more: https://github.com/cyu/rack-corsRails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'

resource '*',
headers: :any,
methods: [:get,:post,:put,:patch,:delete,:options,:head]
end
end

Then in your gem file uncomment rack-cors gem

//Gemfile
# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
gem 'rack-cors'

Then in the terminal run

Bundle install

You will notice that the change was just to uncomment a couple of lines of code and change origins to *. This will allow access to all the resources regardless of where the request is coming from.

Now lets enable BCrypt and Add JWT gem. In your gem file let’s uncomment the BCrypt gem.

//Gemfile
# Use Active Model has_secure_password
gem 'bcrypt', '~> 3.1.7'

Now let’s add the jwt gem by running the following command in your terminal.

Bundle add JWT

Now that we have basic setup, lets create the user model to login

rails g model user username:string password_digest:string 

let’s run the new migration in the terminal

rails db:migrate

and in the User model

//app/models/user.rb
class User < ApplicationRecord
has_secure_password
end

let’s create an authentication controller

rails g controller Authentication

Before continuing working in our authentication controller, lets create the some methods that we are going to need in our application_controller.rb

class ApplicationController < ActionController::API
before_action :authorize
def authorize
@headers = request.headers
if @headers['Authorization'].present?
token = @headers['Authorization'].split(' ').last
decoded_token = decode(token)
@user = User.find_by(id:decoded_token[:user_id])
render json:{ error: 'Not Authorized' }, status:401 unless @user
else
render json: { error: 'Not Authorized' }, status: 401
end
end

private
def encode(payload, exp = 24.hours.from_now)
payload[:exp] = exp.to_i
JWT.encode(payload, Rails.application.secrets.secret_key_base)
end
def decode(token)
body =
JWT.decode(token,Rails.application.secrets.secret_key_base)[0]
HashWithIndifferentAccess.new body
rescue
nil
end
end

In here we have three methods:

  1. encode, use to create the token, it takes a payload which is a hash where we are going to be passing the user id, and expiration time that sets the lifespan of the token.
  2. decode, use te decode the token and extract the user id that was previously encode.
  3. authorize, we are going to run before almost every action to access resources. It would take the token and decode it get the user id and validate if such user id exist in the database to allow access to all resources

The before_action makes sure to run the authorize method before every action of all the controllers that inherit from ApplicationController.

Now we can continue on working in our authentication controller. Inside our authentication method

class AuthenticationController < ApplicationController
skip_before_action :authorize

def authenticate
user = User.find_by(username:params[:username])
if user && user.authenticate(params[:password])
token = encode(user_id: user.id)
render json: {token:token,user:user}
else
render json: {error:"An Error Happened"}
end
end
end

Now that we have all the code,we can test it out. Let’s create an user in the rails console, username: gary1690 and password:12345

Now that our new user is created we can authenticate it. I will be using postman for this. I will send a request to https://localhost:3000/authenticate and send the username and password of the user we previously created. The result is shown below.

Now that we authenticate our user, let’s create son resources to access with our token

rails g reources Items name:string

then run our migration

rails db:migrate

and create our index action in the ItemsController.rb

class ItemsController < ApplicationController
def index
render json: Item.all
end
end

Now we can request our resources using the token

We successfully got the list of items back, you can try creating items to get a full list instead of an empty array.If you do you will get something like this

--

--