API Authentication based on JWT Token in Rails

In this blog, I’m going to share some information about API (Application Programming Interface) Authentication. We used token-based authentication to authenticate users with API at the time. This blog’s demo is available on Github.

What is the use of API?

Authentication can be accomplished in one of two ways. These are below:

  1. Session-Based Authentication
  2. Token-Based Authentication

Session-Based Authentication:

Token-Based Authentication:

Token-based authentication is the most general way to authenticate an API. There are some methods for implementing API authentication, including JWT, Devise, Warden, and so on.

JWT was used to authenticate users in this case. What you must do in this case when a user logs in and receives a token. And you’ll need to transfer a generated token with each request. The generated token is used to determine whether or not the user is authenticated, and we get the current user by using it.

In this blog, you could discover ways to sign up, verify, and log in for users. And after that how to authenticate users create articles. I’ve used ruby version 2.5.1 and the rails version is 5.2.6

Get Started:

rails new article-app --api

When users make a new application using — api, no middleware is generated, such as views, helpers, or asserts. Instead of inheriting from ActionController::Base, ApplicationController inherits from ActionController::API.

Create a User model. The user logs into the application after registering and verifying their email address. Let’s take a look at how you can do it:

rails g model user name email password_digest confirmation_token confirmed_at confirmation_sent_at

to start run the migration:

rails db:migrate

By using these approaches, we can construct a user model with fields for name, e-mail, and password, as well as migrate the schema into the database. Users must fill out these fields in order to register and login into the application.

We’ve added confirmation fields to the database for email address verification. If you want to skip confirming your email, there’s no need to add confirmation fields to your database.

For Keeping the credentials of your users secure. The bcrypt gem was added.

Let’s install bcrypt to protect user passwords. Have such a gem in your gemfile.

# Gemfilegem ‘bcrypt’

After including the bcrypt gem to your gemfile. For installation run:

bundle install

Following the setup needs to be performed after installation. you must complete the configuration process:

The method has_secure_password that should be introduced to set and authenticate passwords. To ensure that the password is well encrypted inside the database, connect this method to the model.

#app/models/user.rbclass User < ApplicationRecord
has_secure_password
end

When the user has been created. Following that, we must send an email to validate the email address.

On the email field, I’ve added present and unique validation.

# app/models/user.rbvalidates :email, presence: true, uniqueness: true

Communicating using email:

# Gemfilegem ‘letter_opener’
gem 'sidekiq'

And install it with the below command:

bundle install

To use ‘letter_opener’, set up the following options.

# config/environments/development.rbconfig.action_mailer.delivery_method = :letter_opener
config.action_mailer.perform_deliveries = true

Set your queuing backend after installing the background job. Add queue_adapter information.

# config/application.rbconfig.active_job.queue_adapter = :sidekiq

Register User:

rails g controller registrations

To sign up users, I’ve used the create action. Make a callback for users before and after the action.

before returning to create confirmation token and after returning to submit confirmation email

# app/controllers/registrations_controller.rbdef create
@user = User.new(user_params)
if @user.save
render json: { success: “Confirmation email send” }, status: :ok
else
render json: { error: @user.errors.messages.map { |msg, desc|
msg.to_s + “ “ + desc[0] }.join(‘,’) }, status: :unauthorized
end
end
private
def user_params
params.require(:users).permit(:name, :email, :password,
:password_confirmation)
end

I added a before_create callback to produce a confirmation token and then send an email to the user to confirm the email address after the user is established. The user model now has a callback.

# app/models/user.rbbefore_create :generate_confirmation_token
after_create :send_confirmation_email
privatedef generate_confirmation_token
self.confirmation_token = SecureRandom.hex(10)
self.confirmation_sent_at = Time.now
end
def send_confirmation_email
SendConfirmationInstructionJob.perform_now(self.confirmation_token)
end

Confirm email :

Copy the confirmation link address and paste it into postman.

Let’s get started with the login section when you’ve confirmed your email.

For that, I used a JWT gem. As a result, we’ll need to include it in our application.

Install JWT

gem ‘jwt’

After that run the below command:

bundle install

For authenticated users, we use the JWT gem to generate authentication tokens. JWT encoding and decoding techniques are used here. I’ve built a service for that function.

To encrypt and decode tokens, I used the ‘HS256’ algorithm.

# app/services/json_web_token_service.rbclass JsonWebTokenService
def self.encode(payload)
JWT.encode payload, Rails.application.secrets.secret_key_base,
‘HS256’
end
def self.decode(token)
JWT.decode( token, Rails.application.secrets.secret_key_base,
algorithm: ‘HS256’)[0]
end
end

You must send the exp key into encoding JWT if you want the token to expire in 24 hours or at any other time. Then, using an integer value, pass exp into the payload. This is how the encoding technique should be.

def self.encode(payload, exp = 24.hours.from_now)
payload[:exp] = exp.to_i
JWT.encode payload, Rails.application.secrets.secret_key_base,
‘HS256’
end

Login User:

rails g controller sessions

I used the create action to log in the users into the application.

# app/controllers/sessions_controller.rbdef create
@user = User.find_by(email: params[:email])
if @user && @user.authenticate(params[:password])
if @user.confirm?
token = JsonWebTokenService.encode({ email: @user.email })
render json: { auth_token: token }
else
render json: { error: “Please verify email address” }, status:
:unauthorized
end
else
render json: { error: “Incorrect Email Or password” }, status:
:unauthorized
end
end

In the user model, a confirm method has been added. This method is a user model instance method.

# app/models/user.rbdef confirm?
confirmed_at?
end

Now check on the request that the user is authenticated or not. For that, I have created the authenticate_helper.rb module under the lib folder.

# lib/authenticate_helper.rbmodule AuthenticateHelper
def authenticate_user
email =
JsonWebTokenService.decode(request.headers[‘HTTP_AUTH_TOKEN’])
[“email”]
@current_user = User.find_by(email: email)
render json: { error: ‘Not Authorized’ }, status: 401 unless
@current_user
end
def user_sign_in?
!!current_user
end
def current_user
@current_user
end
end

The files of the lib directory must be included when the Rails application loads to ensure that everything works. I’ve inserted a lib file into the application.rb file.

# config/application.rbDir[‘./lib/**/*.rb’].each { |file| require file }

Include the module in application_contoller.rb as well.

# app/controllers/application_controller.rbinclude AuthenticateHelper

Add the authenticate_user method to application_controller.rb as a before_action to ensure that the user is authenticated for each request.

# app/controllers/application_controller.rbbefore_action :authenticate_user

Bypass authenticate_user method when the user attempts to login into the application. The create function in session_controller.rb is used for login. We omit the authenticate_user method for creating.

# app/controllers/sessions_controller.rbskip_before_action :authenticate_user, only: [:create]

Start with Article:

rails g model article title description user:references

Then run the following command:

rails db:migrate

As a result, users have a large number of articles. has_may relationship that has been delivered here.

# app/models/user.rbhas_many :articles

Let’s make an article controller. Using the controllers shown below, you can easily generate articles.

rails g controller articles

Articles have been added to the routes.

# config/routes.rbresources :articles, only: [ :create, :show ]

Let’s have a look at how to generate and display articles.

# app/controllers/articles_controller.rbdef create
@article = current_user.articles.new(article_params)
if @article.save
render json: { success: “successfully created” }, status: :ok
else
render json: { error: @article.errors.messages.map { |msg, desc|
msg.to_s + “ “ + desc[0] }.join(‘,’) }, status: :not_found
end
end
def show
@article = current_user.articles.find(params[:id])
render json: { article: @article }, status: :ok
end
privatedef article_params
params.require(:articles).permit(:title, :description)
end

In order to create an article, you must provide the auth token in the header. I’ve included a screenshot below:

Send related information to the body and then press the send button:

You must also pass an auth token in the header when displaying a specific article.

You may modify JSON for articles so that the user’s name is used instead of the user’s id. You may do this by using an Active Model Serializer or Jbuilder.

Active Model Serializers ( Which create JSON responses from model layers ) and Jbuilder ( Creating JSON responses from the View layer, default option provided from Rails ).

I hope I’ve covered everything there is to know about API authentication. On GitHub, I’ve built an article program repository. And do not hesitate to contact me if you have any questions. Thank you for taking the time to read this guide.

Software Engineer