React-Rails authentication API with Devise and Devise-jwt(Back-end) part.

Introduction

I know that you might tried a lot of articles and searched a lot and you may be tired, or my article may be long and you may want to jump to the code right away but let me tell you that this article is all you need to build a co…


This content originally appeared on DEV Community and was authored by Ari Karim

Introduction

I know that you might tried a lot of articles and searched a lot and you may be tired, or my article may be long and you may want to jump to the code right away but let me tell you that this article is all you need to build a complete back-end authentication for your website.

But first let me tell you a brief history about authentication API and how it works.

It is this simple steps:

1- User logs in to the website

2- You create a token for the user and send it back.

3- with each request the user perform you send back the token.

Explanation of the above steps:

1-2- When the user logs in or signs up into your website your back-end no matter which program you use(Rails, Node...) will create a token and send it back to the user in the Response headers called authorization.

3- with each request that the user perform in order for the back-end to know that this person is logged in and it is the correct user, we send back the token in the request headers mainly like Authorization: Bearer Your token here, the back-end se the token and if it was the right token, it will allow you to perform your request, pretty simple isn't it? :).

Lets begin.

  • First lets create a new project, so run this:
Rails new App-Name --api -d postgresql

then cd inside the project.

we will use devise and devise-jwt and rack-cors gems for authentication, so put these gems inside the Gemfile:

Gem 'devise'
Gem 'devise-jwt'
Gem 'rack-cors'
gem 'dotenv-rails' // for hiding variables

Then run

bundle Install or bundle

config/initializers/cors.rb is generated by default when using the--api to create an app. Otherwise you need to create it.

Update it like this. The key here is allowing all origins to make requests:

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'

    resource '*', headers: %w(Authorization),
                  methods: :any,
                  expose: %w[Authorization]
  end
end

Using origins ‘*’ is for our convenience. When deploying to production, set origins to the URL of your front-end app. Otherwise the whole internet will be able to hit your API. Though in some cases that’s desirable.

Lets install Devise

run this command:

Rails g devise:install

Now create a model and migration with devise, and migrate to generate tables.

 rails g devise User
 rails db:setup

Update the generated User model with the following code:

class User < ApplicationRecord

  devise :database_authenticatable,
         :jwt_authenticatable,
         :registerable,
         jwt_revocation_strategy: JwtDenylist
end

Create another model file called jwt_denylist.rb and paste in the following:

class JwtDenylist < ApplicationRecord
  include Devise::JWT::RevocationStrategies::Denylist

  self.table_name = 'jwt_denylist'
end

The included module was previously called Devise::JWT::RevocationStrategies::Blacklist so you may see that in older tutorials.

Create a migration to go along with it.

$ rails g migration CreateJwtDenylist

Update it to this.

class CreateJwtDenylist < ActiveRecord::Migration[6.1]
  def change
    create_table :jwt_denylist do |t|
      t.string :jti, null: false
      t.datetime :exp, null: false
    end
    add_index :jwt_denylist, :jti
  end
end

And migrate.

$ rails db:migrate

This sets up a table which tracks JWT tokens that have been logged out and should no longer have access to the app.

Controllers

Create a sessions controller at /controllers/users/sessions_controller.rb. We want to override the default devise sessions controller so we can specify a custom response on log-in and log-out.

class Users::SessionsController < Devise::SessionsController
  respond_to :json

  private

  def respond_with(resource, _opts = {})
    render json: { message: 'You are logged in.' }, status: :ok
  end

  def respond_to_on_destroy
    log_out_success && return if current_user

    log_out_failure
  end

  def log_out_success
    render json: { message: "You are logged out." }, status: :ok
  end

  def log_out_failure
    render json: { message: "Hmm nothing happened."}, status: :unauthorized
  end
end

Create a new registration controller, /controllers/users/registrations_controller.rb.

class Users::RegistrationsController < Devise::RegistrationsController
  respond_to :json

  private

  def respond_with(resource, _opts = {})
    register_success && return if resource.persisted?

    register_failed
  end

  def register_success
    render json: { message: 'Signed up sucessfully.' }
  end

  def register_failed
    render json: { message: "Something went wrong." }
  end
end

Add one more controller, controllers/members_controller.rb, so we can test logged-in VS logged-out behaviour on an endpoint that required authenticating.

class MembersController < ApplicationController
  before_action :authenticate_user!

  def show
    render json: { message: "Yeppa you did it" }
  end
end

More Devise Setup

Update config/initializers/devise.rb. Add this to the file inside the config block.

config.jwt do |jwt|
  jwt.secret = ENV['RAILS-SECRET-KEY']
end

This tells Devise-JWT to use a secret key specified in our credentials file to build tokens.

Now generate a secret key. And note the output. We’ll add this into our .env file, so run:

rake secret

In the root of the project create a .env file and put this:

RAILS-SECRET-KEY = Your secret key from above command

Note: If you publish this website and upload it to heroku, do not forget to add a Config variable to your app in heroku, simply go to heroku, go to your app, click on setting, click on config vars then put your RAILS-SECRET-KEY then your secret key.

Routes

Update your routes so they point to your new controllers, rather than to the default devise controllers.

Rails.application.routes.draw do
  devise_for :users,
             controllers: {
                 sessions: 'users/sessions',
                 registrations: 'users/registrations'
             }
  get '/member-data', to: 'members#show'
end

Now you will have these end-points:

http//localhost:3000/users/sign_in

    Rooute ==> Sign in
    Method ==> POST
    Body ==> { "user": { "email": "test@example.com", "password": "12345678" } }
    Response token ==> data.headers.authorization

http//localhost:3000/users

    Route ==> Sign up
    Method ==> POST
    Body ==> { "user": { "email": "test@example.com", "password": "12345678" } }
    Response token ==> data.headers.authorization

http//localhost:3000/member

    Route ==> To know if user logged in?
    Method ==> GET
    headers ==> token: token you saved from log in or sign up user
    Response ==> data.data.message=> 'yeppa you did it.'

http//localhost:3000/users/sign_out

    Route ==> To know if user logged in?
    Method ==> DELETE
    headers ==> token: token you saved from log in or sign up user
    Response ==> data.data.message=> 'You are logged out.'

Now you can use postman,Insomnia... or any other tool to test the endpoints, just don't forget that except signin and signup you need to send the token in the header like this:
Authorization: Bearer Your token here

You may ask what will happen to the token when the user signs out? do you remember the JWT_denylist model that we created!,
each time the user logs out rails puts his token inside that table and each time you log in and have a request with a token, rails compare your token with the tokens inside the deny_list table if it matches one of them it means you are logged out and you are not authorized.


This content originally appeared on DEV Community and was authored by Ari Karim


Print Share Comment Cite Upload Translate Updates
APA

Ari Karim | Sciencx (2021-07-25T16:19:33+00:00) React-Rails authentication API with Devise and Devise-jwt(Back-end) part.. Retrieved from https://www.scien.cx/2021/07/25/react-rails-authentication-api-with-devise-and-devise-jwtback-end-part/

MLA
" » React-Rails authentication API with Devise and Devise-jwt(Back-end) part.." Ari Karim | Sciencx - Sunday July 25, 2021, https://www.scien.cx/2021/07/25/react-rails-authentication-api-with-devise-and-devise-jwtback-end-part/
HARVARD
Ari Karim | Sciencx Sunday July 25, 2021 » React-Rails authentication API with Devise and Devise-jwt(Back-end) part.., viewed ,<https://www.scien.cx/2021/07/25/react-rails-authentication-api-with-devise-and-devise-jwtback-end-part/>
VANCOUVER
Ari Karim | Sciencx - » React-Rails authentication API with Devise and Devise-jwt(Back-end) part.. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/07/25/react-rails-authentication-api-with-devise-and-devise-jwtback-end-part/
CHICAGO
" » React-Rails authentication API with Devise and Devise-jwt(Back-end) part.." Ari Karim | Sciencx - Accessed . https://www.scien.cx/2021/07/25/react-rails-authentication-api-with-devise-and-devise-jwtback-end-part/
IEEE
" » React-Rails authentication API with Devise and Devise-jwt(Back-end) part.." Ari Karim | Sciencx [Online]. Available: https://www.scien.cx/2021/07/25/react-rails-authentication-api-with-devise-and-devise-jwtback-end-part/. [Accessed: ]
rf:citation
» React-Rails authentication API with Devise and Devise-jwt(Back-end) part. | Ari Karim | Sciencx | https://www.scien.cx/2021/07/25/react-rails-authentication-api-with-devise-and-devise-jwtback-end-part/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.