Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
July 25, 2021 04:19 pm GMT

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 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]  endend

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 thats 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: JwtDenylistend

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  endend

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  endend

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." }  endend

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" }  endend

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. Well 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": "[email protected]", "password": "12345678" } }    Response token ==> data.headers.authorizationhttp//localhost:3000/users    Route ==> Sign up    Method ==> POST    Body ==> { "user": { "email": "[email protected]", "password": "12345678" } }    Response token ==> data.headers.authorizationhttp//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.


Original Link: https://dev.to/arikarim/react-rails-authentication-api-with-devise-and-devise-jwt-back-end-part-dj

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To