React Node Jwt Authentication without Redux – using LocalStorage and Axios

Reactjs-Nodejs-Jwt-Authentication-without-Redux-using-Axios-and-LocalStorage

Tutorial: React Node Jwt Authentication (without Redux) – using LocalStorage and Axios (plus interceptor) in React application and Express + Sequelize + MySQL/PostgreSQL in Nodejs backend solution.

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. And “How to build Reactjs Nodejs Jwt Token Based Authentication Example?” is one of the most common questions for Nodejs development world. So in the tutorial, I introduce how to implement an application “Reactjs JWT Nodejs token Authentication Example” with details step by step and 100% running sourcecode.

– I give you an Epic of the application, a fullstack excutive flow from frontend (Reactjs) to backend (Nodejs/Express) to database (MySQL/PostgreSQL) with overall architecture diagram.
– I give you a layer diagram of Reactjs Jwt Application with LocalStorage and Axios (plus Interceptor)
– I guide you detail-steps how to implement a security Jwt Token Nodejs backend.
– I guide you step by step how to develop a Reactjs Jwt Authentication application.
– Finally, I do an integrative testing from Reactjs Jwt Authentication application to jwt Nodejs Security RestAPIs.

Related posts:


Contents

Video Guide – How to build React Node Jwt Token Authentication Example

Overview Reactjs Jwt Nodejs Token Authentication Example

JWT Token Introduction

JSON Web Token (JWT) defines a compact and self-contained way for securely transmitting information between parties as a JSON object.

Scenarios where JSON Web Tokens are useful:

  • Authorization: the most common scenario for using JWT. Single Sign On is a feature that widely uses JWT
  • Information Exchange: Because JWTs can be signed, JSON Web Tokens are a good way of securely transmitting information between parties.

JSON Web Tokens consist of 3 parts:

  • Header
  • Payload
  • Signature

-> JWT looks like Header-Base64-String.Payload-Base64-String.Signature-Base64-String

Header consists of two parts:

  • token type.
  • hashing algorithm.

-> Example:

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload contains the claims. Claims are statements about an entity and additional information.
There are 3 types of claims ->

  • Registered claims -> These are a set of predefined claims: iss (issuer), exp (expiration time), sub (subject)
  • Public claims
  • Private claims

Example:

{
  "sub": "thomasgkz",
  "iat": 1537603195,
  "exp": 1537689595
}

Signature -> To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.

Example:

HMACSHA512(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret
)

Combine all together, we get 3 Base64-URL strings separated by dots,

-> Example:

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0aG9tYXNna3oiLCJpYXQiOjE1Mzc2MDMxOTUsImV4cCI6MTUzNzY4OTU5NX0.m2YMjTYmOnfR7nnVNxqCzWbQ2FhKRe1eiizxnC2TF4eAoEzKlwo7PheVkKcxj08ST3vB-ZOIhiORvYVfSgzcog

When accessing a protected route or resource, the user agent should send the JWT, typically in the Authorization header using the Bearer schema.

Example:

Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0aG9tYXNna3oiLCJpYXQiOjE1Mzc2MDMxOTUsImV4cCI6MTUzNzY4OTU5NX0.m2YMjTYmOnfR7nnVNxqCzWbQ2FhKRe1eiizxnC2TF4eAoEzKlwo7PheVkKcxj08ST3vB-ZOIhiORvYVfSgzcog

Overall Jwt Login System Architecture Diagram

Overall Diagram - Reactjs JWT Authentication
Overall Diagram – Reactjs JWT Authentication

For the Reactjs JWT Authentication tutorial, we have 2 projects:
– Backend project Nodejs/Express provides secured RestAPIs with JWT token.
– Reactjs project will request RestAPIs from Nodejs with the Jwt Token Authentication implementation.

JWT Authentication Sequence Diagram

The diagram below show how our system handles User Registration and User Login processes:

Reactjs Node Jwt Authentication Working Process Diagram
Reactjs Node Jwt Authentication Working Process Diagram

1. User Registration Phase:
– User uses a React.js register form to post user’s info (name, username, email, role, password) to Backend API /api/auth/signup.
– Backend will check the existing users in database and save user’s signup info to database. Finally, It will return a message (successfully or fail) to

2. User Login Phase:
– User posts user/password to signin to Backend RestAPI /api/auth/signin.
– Backend will check the username/password, if it is right, Backend will create and JWT string with secret then return it to Reactjs client.

After signin, user can request secured resources from backend server by adding the JWT token in Authorization Header. For each request, backend will check the JWT signature and then returns back the resources based on user’s registered authorities.

Reactjs JWT Authentication Diagram Overview

Reactjs Node JWT Authentication Layer Diagram
Reactjs Node JWT Authentication Layer Diagram

Reactjs JWT Authentication would be built with 5 main kind blocks:

  • Reactjs Router is a standard library for routing in React. It enables the navigation among views of various components in a React Application, allows changing the browser URL, and keeps the UI in sync with the URL.
  • Reactjs Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.
  • Reactjs Service is a bridge between Reactjs Component and Backend Server, it is used to do technical logic with Backend Server (using Ajax Engine to fetch data from Backend, or using Local Storage to save user login data) and returned a response data to React.js Components
  • Local Storage allow to save key/value pairs in a web browser. It is a place to save the login user’s info.
  • Axios – (an Ajax Engine) is a promise-based HTTP client for the browser and Node. js. Axios makes it easy to send asynchronous HTTP requests to REST endpoints and perform CRUD operations.

Jwt Nodejs Token Security RestAPIs Diagram Overview

Reactjs Nodejs Jwt Authentication Architecture Diagram Back End Server
Reactjs Nodejs Jwt Authentication Architecture Diagram Back End Server

HTTP request that matches route will be accepted by CORS Middleware before coming to Security layer.

Security layer includes:
– JWT Authentication Middleware: verify SignUp, verify token
– Authorization Middleware: check User’s roles

Main Business Logic Processing interacts with database via Sequelize and send HTTP response (token, user information, data based on roles…) to client.

Project Goal

We create a Reactjs JWT Authentication project as below:

Reactjs Jwt Authentication project structure
Reactjs Jwt Authentication project structure

It includes 8 components and 2 services and a router in app.js file.

– Home page:

Reactjs Home Page
Reactjs Home Page

– User Register page:

Reactjs jwt authentication - signup Jack with user role
Reactjs jwt authentication – signup Jack with user role

– Login Page:

reactjs jwt nodejs authentication wrong login user validation
reactjs jwt nodejs authentication wrong login user validation

– Profile Page:

reactjs user profile info
reactjs user profile info

– Use Page:

reactjs nodejs jwt authentication - user content info
reactjs nodejs jwt authentication – user content info

– Project Manager Page:

Profile page of Adam user
Profile page of Adam user

– Reactjs Admin page:

Admin Content page
Admin Content page

Token Based Authentication in Node.js RestAPIs Implementation

Review Nodejs Jwt Architecture Diagram

Reactjs Nodejs Jwt Authentication Architecture Diagram Back End Server
Reactjs Nodejs Jwt Authentication Architecture Diagram Back End Server

Details check the above session: Jwt Nodejs Token Security RestAPIs Diagram Overview

Configure Nodejs/Express Middleware & Restapis

module.exports = function (app) {
 
  const controller = require('../controller/controller.js');
 
  app.use(function (req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "x-access-token, Origin, Content-Type, Accept");
    next();
  });
 
  app.post('/api/auth/signup', [verifySignUp.checkDuplicateUserNameOrEmail, verifySignUp.checkRolesExisted], controller.signup);
 
  app.post('/api/auth/signin', controller.signin);
 
  app.get('/api/test/user', [authJwt.verifyToken], controller.userContent);
 
  app.get('/api/test/pm', [authJwt.verifyToken, authJwt.isPmOrAdmin], controller.managementBoard);
 
  app.get('/api/test/admin', [authJwt.verifyToken, authJwt.isAdmin], controller.adminBoard);
}

– For HTTP Header, we allow x-access-token for JWT.
– When a HTTP request call /signup api, it will also be passed to checkDuplicateUserNameOrEmail() and checkRolesExisted() funtions before going to controller’s signup() funtion.
– JWT Authentication middleware with verifyToken() and role checking funtions (isPmOrAdmin, isAdmin) will be called before controller returns authorized data (based on roles).

Jwt Nodejs Generate Token

Inside controller’s signin() funtion, we use sign() funtion from jsonwebtoken:

var jwt = require('jsonwebtoken');
 
exports.signin = (req, res) => {
  User.findOne({
    where: { username: req.body.username }
  }).then(user => {
    // check user & password...
 
    var token = jwt.sign({ id: user.id }, config.secret, {
      expiresIn: 86400 // expires in 24 hours
    });
 
    // get other user information
    res.status(200).send({
      auth: true,
      accessToken: token,
      username: user.username,
      authorities: authorities
    });
  });
}

Nodejs Jwt Verify Token

We get token from x-access-token of HTTP headers, then use verify() function of jsonwebtoken:

const jwt = require('jsonwebtoken');
 
verifyToken = (req, res, next) => {
  let token = req.headers['x-access-token'];
  
  if (!token){ // notice that no token was provided...}
 
  jwt.verify(token, 'SECRET KEY', (err, decoded) => {
    if (err){
      return res.status(500).send({ 
          auth: false, 
          message: 'Fail to Authentication. Error -> ' + err 
        });
    }
    req.userId = decoded.id;
    next();
  });
}

Jwt Nodejs User & Roles Relationship model

We define Role & User Sequelize models as below:

Reactjs Nodejs Jwt Authentication - Sequelize Many to many User Role Relationship
Reactjs Nodejs Jwt Authentication – Sequelize Many to many User Role Relationship

Implementation of the Many-to-Many relationship:

// user.model.js
module.exports = (sequelize, Sequelize) => {
  const User = sequelize.define('users', {
    firstname: {
      type: Sequelize.STRING
    },
	lastname: {
      type: Sequelize.STRING
    },
    username: {
      type: Sequelize.STRING
    },
    email: {
      type: Sequelize.STRING
    },
    password: {
      type: Sequelize.STRING
    }
  });
  
  return User;
}
 
// role.model.js
module.exports = (sequelize, Sequelize) => {
  const Role = sequelize.define('roles', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true
    },
    name: {
      type: Sequelize.STRING
    }
  });
  
  return Role;
}
 
// db.config.js
const Sequelize = require('sequelize');
const sequelize = new Sequelize(...);
 
const db = {};
 
db.Sequelize = Sequelize;
db.sequelize = sequelize;
 
db.user = require('../model/user.model.js')(sequelize, Sequelize);
db.role = require('../model/role.model.js')(sequelize, Sequelize);
 
db.role.belongsToMany(db.user, { through: 'user_roles', foreignKey: 'roleId', otherKey: 'userId'});
db.user.belongsToMany(db.role, { through: 'user_roles', foreignKey: 'userId', otherKey: 'roleId'});
 
module.exports = db;

Nodejs jwt token Project Goal

The diagram below show how our system handles User Registration and User Login processes:

Reactjs Node Jwt Authentication Working Process Diagram
Reactjs Node Jwt Authentication Working Process Diagram

– SignUp /api/auth/signup:

Reactjs jwt authentication - signup Jack with user role
Reactjs jwt authentication – signup Jack with user role

– Sign In /api/auth/signin:

reactjs jwt nodejs authentication wrong login user validation
reactjs jwt nodejs authentication wrong login user validation

– Access User Page /api/test/user:

reactjs nodejs jwt authentication - user content info
reactjs nodejs jwt authentication – user content info

– Access PM Page /api/test/pm:

Reactjs JWT Authentication - PM Content
Reactjs JWT Authentication – PM Content

– Access to Admin page /api/test/admin:

Admin Content page
Admin Content page

Technologies

– Nodejs/Express
– Json Web Token
– BCryptjs
– Sequelize
– MySQL

Nodejs Jwt Token Project Structure

Nodejs jwt authentication project structure
Nodejs jwt authentication project structure

config package defines MySQL Database Configuration, JWT Secret Key & User Roles.
model package defines Role & User Sequelize models.
router package defines RestAPI URLs, verification functions for signup api, JWT verification for signin api, and authorization functions for content requested by user roles.
controller package defines process functions for each RestAPIs declared in router package.

Create Nodejs Project

Install Express, Sequelize, MySQL, Json Web Token, Bcryptjs:

$npm install express sequelize mysql2 jsonwebtoken bcryptjs --save

package.json

{
  "name": "nodejs-jwt-authentication",
  "version": "1.0.0",
  "description": "Nodejs JWT Authentication project",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "Nodejs",
    "JWT",
    "Authentication",
    "Express",
    "RestAPIs"
  ],
  "author": "https://loizenai.com",
  "license": "ISC",
  "dependencies": {
    "bcryptjs": "^2.4.3",
    "express": "^4.17.1",
    "jsonwebtoken": "^8.5.1",
    "mysql2": "^2.1.0",
    "sequelize": "^5.21.12"
  }
}

Create Nodejs Sequelize Models

model/user.model.js

module.exports = (sequelize, Sequelize) => {
  const User = sequelize.define('users', {
    lastname: {
      type: Sequelize.STRING
    },
    firstname: {
      type: Sequelize.STRING
    },
    username: {
      type: Sequelize.STRING
    },
    email: {
      type: Sequelize.STRING
    },
    password: {
      type: Sequelize.STRING
    }
  });
  
  return User;
}

model/role.model.js

module.exports = (sequelize, Sequelize) => {
  const Role = sequelize.define('roles', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true
    },
    name: {
      type: Sequelize.STRING
    }
  });
  
  return Role;
}

Nodejs Sequelize Database Configuration

config/env.js

For MySQL database:

 
const env = {
  database: 'loizenaidb',
  username: 'root',
  password: '12345',
  host: 'localhost',
  dialect: 'mysql',
  pool: {
    max: 5,
    min: 0,
    acquire: 30000,
    idle: 10000
  }
};
 
module.exports = env;

For PostgreSQL database:

const env = {
  database: 'loizenai',
  username: 'postgres',
  password: '123',
  host: 'localhost',
  dialect: 'postgres',
  pool: {
    max: 5,
    min: 0,
    acquire: 30000,
    idle: 10000
  }
};
 
module.exports = env;

Define RestAPIs Router with Middleware

Nodejs RestAPIs Router

router/router.js

const verifySignUp = require('./verifySignUp');
const authJwt = require('./verifyJwtToken');
 
module.exports = function (app) {
 
  const controller = require('../controller/controller.js');
 
  app.use(function (req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "x-access-token, Origin, Content-Type, Accept");
    next();
  });
 
  app.post('/api/auth/signup', [verifySignUp.checkDuplicateUserNameOrEmail, verifySignUp.checkRolesExisted], controller.signup);
 
  app.post('/api/auth/signin', controller.signin);
 
  app.get('/api/test/user', [authJwt.verifyToken], controller.userContent);
 
  app.get('/api/test/pm', [authJwt.verifyToken, authJwt.isPmOrAdmin], controller.managementBoard);
 
  app.get('/api/test/admin', [authJwt.verifyToken, authJwt.isAdmin], controller.adminBoard);
}

Nodejs/Express Middleware functions

router/verifySignUp.js

const db = require('../config/db.config.js');
const config = require('../config/config.js');
const ROLEs = config.ROLEs;
const User = db.user;
 
checkDuplicateUserNameOrEmail = (req, res, next) => {
  // -> Check Username is already in use
  User.findOne({
    where: {
      username: req.body.username
    }
  }).then(user => {
    if (user) {
      res.status(400).send("Fail -> Username is already taken!");
      return;
    }
 
    // -> Check Email is already in use
    User.findOne({
      where: {
        email: req.body.email
      }
    }).then(user => {
      if (user) {
        res.status(400).send("Fail -> Email is already in use!");
        return;
      }
 
      next();
    });
  });
}
 
checkRolesExisted = (req, res, next) => {
  for (let i = 0; i < req.body.roles.length; i++) {
    if (!ROLEs.includes(req.body.roles[i].toUpperCase())) {
      res.status(400).send("Fail -> Does NOT exist Role = " + req.body.roles[i]);
      return;
    }
  }
  next();
}
 
const signUpVerify = {};
signUpVerify.checkDuplicateUserNameOrEmail = checkDuplicateUserNameOrEmail;
signUpVerify.checkRolesExisted = checkRolesExisted;
 
module.exports = signUpVerify;

router/verifyJwtToken.js

const jwt = require('jsonwebtoken');
const config = require('../config/config.js');
const db = require('../config/db.config.js');
const User = db.user;
 
verifyToken = (req, res, next) => {
  let token = req.headers['x-access-token'];
  
  if (!token){
    return res.status(403).send({ 
      auth: false, message: 'No token provided.' 
    });
  }
 
  jwt.verify(token, config.secret, (err, decoded) => {
    if (err){
      return res.status(500).send({ 
          auth: false, 
          message: 'Fail to Authentication. Error -> ' + err 
        });
    }
    req.userId = decoded.id;
    next();
  });
}
 
isAdmin = (req, res, next) => {  
  User.findById(req.userId)
    .then(user => {
      user.getRoles().then(roles => {
        for(let i=0; i<roles.length; i++){
          console.log(roles[i].name);
          if(roles[i].name.toUpperCase() === "ADMIN"){
            next();
            return;
          }
        }
        
        res.status(403).send("Require Admin Role!");
        return;
      })
    })
}
 
isPmOrAdmin = (req, res, next) => {
  User.findById(req.userId)
    .then(user => {
      user.getRoles().then(roles => {
        for(let i=0; i<roles.length; i++){          
          if(roles[i].name.toUpperCase() === "PM"){
            next();
            return;
          }
          
          if(roles[i].name.toUpperCase() === "ADMIN"){
            next();
            return;
          }
        }
        
        res.status(403).send("Require PM or Admin Roles!");
      })
    })
}
 
const authJwt = {};
authJwt.verifyToken = verifyToken;
authJwt.isAdmin = isAdmin;
authJwt.isPmOrAdmin = isPmOrAdmin;
 
module.exports = authJwt;

Implement Express RestApis Controller

controller/controller.js

const db = require('../config/db.config.js');
const config = require('../config/config.js');
const User = db.user;
const Role = db.role;

const Op = db.Sequelize.Op;

var jwt = require('jsonwebtoken');
var bcrypt = require('bcryptjs');

exports.signup = (req, res) => {
	// Save User to Database
	User.create({
		name: req.body.name,
		username: req.body.username,
		email: req.body.email,
		password: bcrypt.hashSync(req.body.password, 8)
	}).then(user => {
		Role.findAll({
			where: {
				name: {
					[Op.or]: req.body.roles
				}
			}
		}).then(roles => {
			user.setRoles(roles).then(() => {
				res.send({ message: 'Registered successfully!' });
			});
		}).catch(err => {
			res.status(500).send({ reason: err.message });
		});
	}).catch(err => {
		res.status(500).send({ reason: err.message });
	})
}

exports.signin = (req, res) => {
	User.findOne({
		where: {
			username: req.body.username
		}
	}).then(user => {
		if (!user) {
			return res.status(404).send({ reason: 'User Not Found.' });
		}

		var passwordIsValid = bcrypt.compareSync(req.body.password, user.password);
		if (!passwordIsValid) {
			return res.status(401).send({ auth: false, accessToken: null, reason: 'Invalid Password!' });
		}

		var token = jwt.sign({ id: user.id }, config.secret, {
			expiresIn: 86400 // expires in 24 hours
		});

		var authorities = [];
		user.getRoles().then(roles => {
			for (let i = 0; i < roles.length; i++) {
				authorities.push('ROLE_' + roles[i].name.toUpperCase());
			}
			res.status(200).send({
				auth: true,
				accessToken: token,
				username: user.username,
				authorities: authorities
			});
		})
	}).catch(err => {
		res.status(500).send({ reason: err.message });
	});
}

exports.userContent = (req, res) => {
	User.findOne({
		where: { id: req.userId },
		attributes: ['name', 'username', 'email'],
		include: [{
			model: Role,
			attributes: ['id', 'name'],
			through: {
				attributes: ['userId', 'roleId'],
			}
		}]
	}).then(user => {
		res.status(200).send({
			'description': '>>> User Contents!',
			'user': user
		});
	}).catch(err => {
		res.status(500).send({
			'description': 'Can not access User Page',
			'error': err
		});
	})
}

exports.adminBoard = (req, res) => {
	User.findOne({
		where: { id: req.userId },
		attributes: ['name', 'username', 'email'],
		include: [{
			model: Role,
			attributes: ['id', 'name'],
			through: {
				attributes: ['userId', 'roleId'],
			}
		}]
	}).then(user => {
		res.status(200).send({
			'description': '>>> Admin Contents',
			'user': user
		});
	}).catch(err => {
		res.status(500).send({
			'description': 'Can not access Admin Board',
			'error': err
		});
	})
}

exports.managementBoard = (req, res) => {
	User.findOne({
		where: { id: req.userId },
		attributes: ['name', 'username', 'email'],
		include: [{
			model: Role,
			attributes: ['id', 'name'],
			through: {
				attributes: ['userId', 'roleId'],
			}
		}]
	}).then(user => {
		res.status(200).send({
			'description': '>>> Project Management Board',
			'user': user
		});
	}).catch(err => {
		res.status(500).send({
			'description': 'Can not access Management Board',
			'error': err
		});
	})
}

We define jwt-secret-key & User Roles in config/config.js:

module.exports = {
  'secret': 'loizenai-super-secret-key',
  ROLEs: ['USER', 'ADMIN', 'PM']
};

Implement Nodejs Server

server.js

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.json())

require('./app/router/router.js')(app);

const db = require('./app/config/db.config.js');

const Role = db.role;

// force: true will drop the table if it already exists
db.sequelize.sync({force: true}).then(() => {
  console.log('Drop and Resync with { force: true }');
  initial();
});

// Create a Server
var server = app.listen(8080, function () {

	var host = server.address().address
	var port = server.address().port

	console.log("App listening at http://%s:%s", host, port)
})

function initial() {
	Role.create({
		id: 1,
		name: "USER"
	});

	Role.create({
		id: 2,
		name: "PM"
	});

	Role.create({
		id: 3,
		name: "ADMIN"
	});
}

Testing Jwt token authentication Nodejs RestAPI

Sign-Up 3 users:

– Jack has ROLE_USER role
– Adam has ROLE_PM & ROLE_USER roles
– Thomas has ROLE_ADMIN role

Reactjs Node JWT Authentication - Register Admin User
Reactjs Node JWT Authentication – Register Admin User

– Check database’s tables:

Check MySQL database after adding Admin user
Check MySQL database after adding Admin user

Signin with normal user

– Jack can access api/test/user url, can NOT access others.

-> Sign In:

JWT Authentication - Jack signin with PostMan to Backend
JWT Authentication – Jack signin with PostMan to Backend

Access Protected Resources:

Jack with USER_ROLE retrieves user resources from backend restapi
Jack with USER_ROLE retrieves user resources from backend restapi
Jack can NOT access PM resources
Jack can NOT access PM resources

Signin with PM user

– Adam can access api/test/user and api/test/pm url.
Can NOT access /api/test/admin url.

-> Sign In:

Adam SignIn successfully
Adam SignIn successfully

-> Access Protected Resources:

Adam can retrieve USER resources
Adam can retrieve USER resources
Adam can access PM resources
Adam can access PM resources
Adam can NOT access Admin resources
Adam can NOT access Admin resources

Signin with Admin user

– Thomas can access all URLs.

-> Sign In:

Thomas Signin
Thomas Signin

-> Access Protected Resource:

Thomas access projected resource
Thomas access projected resource

Reactjs JWT Token Authentication Implementation

Review Reactjs Login Architecture Diagram

Reactjs Node JWT Authentication Layer Diagram
Reactjs Node JWT Authentication Layer Diagram

More details you can see more at above session: Reactjs JWT Authentication Diagram Overview

Step by step to build Reactjs JWT Authentication Application

>>> image

For building the Reactjs JWT Authentication, we do below steps:

  • We create Reactjs Jwt Authentication application
  • We implement React.js Authentication Service. It provides RestAPIs to signin/signup and retrieve user’s login info from Local Storage
  • We implement React.js Backend Service. It provides RestAPIs to retrieve data from Security Backend
  • We implement Reactjs components to signIn/signUp and show data from backend server
  • We define a Reactjs router to navigate between UI components

Setup Reactjs Project

Create React App is a command line utility that generates React projects for you. It’s a convenient tool because it also offers commands that will build and optimize your project for production.

The create-react-app will set up everything you need to run a React application.

– Create a new project in the app directory with Yarn.

yarn create react-app Reactjs-Jwt-Authentication

Project Structure:

Start Setup React project structure
Start Setup React project structure

More details you can see at: Create Reactjs Project

After the app creation process completes, navigate into the app directory and install Bootstrap, React Router, and Reactstrap.

Reactstrap: This library contains React Bootstrap 4 components that favor composition and control. The library does not depend on jQuery or Bootstrap javascript.
– React Router: Components are the heart of React’s powerful, declarative programming model. React Router is a collection of navigational components that compose declaratively with your application.


cd Reactjs-Jwt-Authentication
yarn add bootstrap react-cookie react-router-dom reactstrap

Implement Reactjs JWT Authentication Service

The service AuthenticationService implements 4 main functions to signup/signin/signout and get current login user’s info:

To do http requests with Backend server, the AuthenticationService use Axios (an HttpClient) to interact with Server. AuthenticationService uses localStorage to store user login’s info.

import axios from "axios";

/**
 * @Copyright by https://loizenai.com
 *        youtube loizenai
 */

class AuthenticationService {
  signin = (username, password) => {
      return axios.post("/api/auth/signin", {username, password})
        .then(response => {
          if (response.data.accessToken) {
            localStorage.setItem("user", JSON.stringify(response.data));
          }
          return response.data;
        })
        .catch(err => {
          console.log(err);
          throw err;
        });
  }

  signOut() {
    localStorage.removeItem("user");
  }

  register = async(firstname, lastname, username, email, password) => {
    return axios.post("/api/auth/signup", {
      firstname,
      lastname,
      username,
      email,
      password
    });
  }

  getCurrentUser() {
    return JSON.parse(localStorage.getItem('user'));;
  }
}

export default new AuthenticationService();

Implement Reactjs jwt Backend Service with Axios interceptor

BackendService is used to get resources from server:
getUserBoard()
getPmBoard()
getAdminBoard()

With each request from BackendService to server, we use axios interceptors to attach the Authorization header with JWT token.

import axios from 'axios';

// Add a request interceptor
axios.interceptors.request.use( config => {
  const user = JSON.parse(localStorage.getItem('user'));

  if(user && user.accessToken){
    const token = 'Bearer ' + user.accessToken;
    config.headers.Authorization =  token;
  }

  return config;
});

class BackendService {
  async getUserBoard() {
    return await axios.get("/api/test/user");
  }

  async getPmBoard() {
    return await axios.get("/api/test/pm");
  }

  async getAdminBoard() {
    return await axios.get("/api/test/admin");
  }
}

export default new BackendService();

Implement Reactjs jwt AppNavbar component

Reactjs jwt authentication - navigation bar
Reactjs jwt authentication – navigation bar
import React, { Component } from 'react';
import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavbarText, NavItem, NavLink } from 'reactstrap';
import { Link } from 'react-router-dom';

import { withRouter } from 'react-router-dom';

import AuthenticationService from '../services/AuthenticationService';

class AppNavbar extends Component {
  constructor(props) {
    super(props);
    this.state = {isOpen: false};
    this.toggle = this.toggle.bind(this);

    this.state = {
      showUser: false,
      showPM: false,
      showAdmin: false,
      username: undefined,
      login: false
    };
  }

  componentDidMount() {
    const user = AuthenticationService.getCurrentUser();

    if (user) {
      const roles = [];

      user.authorities.forEach(authority => {
        roles.push(authority.authority)
      });
  
      this.setState({
        showUser: true,
        showPM: roles.includes("ROLE_PM") || roles.includes("ROLE_ADMIN"),
        showAdmin: roles.includes("ROLE_ADMIN"),
        login: true,
        username: user.username
      });
    }
  }

  signOut = () => {
    AuthenticationService.signOut();
    this.props.history.push('/home');
    window.location.reload();
  }

  toggle() {
    this.setState({
      isOpen: !this.state.isOpen
    });
  }

  render() {
    return <Navbar color="dark" dark expand="md">
      <NavbarBrand tag={Link} to="/home">Loizenai.com</NavbarBrand>
      <Nav className="mr-auto">
        <NavLink href="/home">Home</NavLink>
        {this.state.showUser && <NavLink href="/user">User</NavLink>}
        {this.state.showPM && <NavLink href="/pm">PM</NavLink>}
        {this.state.showAdmin && <NavLink href="/admin">Admin</NavLink>}
      </Nav>
      <NavbarToggler onClick={this.toggle}/>
      <Collapse isOpen={this.state.isOpen} navbar>
        {
          this.state.login ? (
            <Nav className="ml-auto" navbar>
              <NavItem>
                  <NavbarText>
                    Signed in as: <a href="/profile">{this.state.username}</a>
                  </NavbarText>
              </NavItem>
              <NavItem>
                <NavLink href="#" onClick={this.signOut}>SignOut</NavLink>
              </NavItem>
            </Nav>                 
          ) : (
            <Nav className="ml-auto" navbar>
              <NavItem>
                <NavLink href="/signin">Login</NavLink>
              </NavItem>
              <NavItem>
                <NavLink href="/signup">SignUp</NavLink>
              </NavItem>
            </Nav>
          )
        }
      </Collapse>
    </Navbar>;
  }
}

export default withRouter(AppNavbar);

– In AppNavbar component, We use componentDidMount() to load user info, and then extracts data from user login info to determine what links would be showed on React.js’s UI.

Implement Reactjs jwt Home component

Reactjs Home Page - signout returned page
Reactjs Home Page – signout returned page
import React, { Component } from 'react';
import AppNavbar from './AppNavbar';
import { Link } from 'react-router-dom';
import { Button, Container } from 'reactstrap';
import { Alert } from "react-bootstrap"

class Home extends Component {

  constructor(props) {
    super(props);
  }

  componentDidMount() {
  }

  render() {
    return (
      <div>
        <AppNavbar/>
        <Container fluid>
          <div style={{marginTop:"20px"}}>
            <Alert variant="primary">
              <h2>Reactjs JWT Authentication Application</h2>
              <Button color="success"><Link to="/signin"><span style={{color:"white"}}>Login</span></Link></Button>
            </Alert>
          </div>
        </Container>
      </div>
    );
  }
}

export default Home;

Implement Reactjs jwt SignUp component

Reactjs jwt authentication - signup Jack with user role
Reactjs jwt authentication – signup Jack with user role

– For posting data from register-form to backend server, SignUp component uses the register(...) function of AuthenticationService service.

SignUp component implements a changeHandler() function to save the values from input fields of Register form to the state object of SignUp component.

– The function changeHandler() also includes segment code to validate input data from register-form:

Reactjs-Nodejs-JWT-Authentication-Register-Form-Validation
Reactjs-Nodejs-JWT-Authentication-Register-Form-Validation

– Detail Coding:

import React, { Component } from 'react';
import AppNavbar from './AppNavbar';
import { Container } from 'reactstrap';
import { Button, Form, FormGroup, Input, Label, Row, Col } from "reactstrap";
import { Alert } from "react-bootstrap"

import Authentication from '../services/AuthenticationService'

const validEmailRegex = RegExp(/^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i);

const validateForm = (errors) => {
  let valid = true;
  Object.values(errors).forEach(
    (val) => val.length > 0 && (valid = false)
  );
  return valid;
}

class SignUp extends Component {
  
  constructor(props) {
    super(props);
    this.state = {
      firstname: "",
      lastname: "",
      username: "",
      email: "",
      password: "",
      message: "",
      successful: false,
      validForm: true,
      errors: {
        firstname: '',
        lastname: '',
        username: '',
        email: '',
        password: ''
      }
    };
  }

  changeHandler = (event) => {
    const { name, value } = event.target;
  
    let errors = this.state.errors;

    switch (name) {
      case 'firstname':
        errors.firstname = 
          value.length < 3
            ? 'FirstName must be 3 characters long!'
            : '';
        break;
      case 'lastname':
        errors.lastname = 
          value.length < 3
            ? 'LastName must be 3 characters long!'
            : '';
        break;
      case 'username':
        errors.username = 
          value.length < 5
            ? 'Username must be 5 characters long!'
            : '';
        break;
      case 'email': 
        errors.email = 
          validEmailRegex.test(value)
            ? ''
            : 'Email is not valid!';
        break;
      case 'password': 
        errors.password = 
          value.length < 8
            ? 'Password must be 8 characters long!'
            : '';
        break;
      default:
        break;
    }
  
    this.setState({errors, [name]: value}, ()=> {
        console.log(errors)
    })  
  }

  signUp = (e) => {
    e.preventDefault();
    const valid = validateForm(this.state.errors);
    this.setState({validForm: valid});
    if(valid){
      Authentication.register(
        this.state.firstname,
        this.state.lastname,
        this.state.username,
        this.state.email,
        this.state.password
      ).then(
        response => {
          this.setState({
            message: response.data.message,
            successful: true
          });
        },
        error => {
          console.log("Fail! Error = " + error.toString());
          
          this.setState({
            successful: false,
            message: error.toString()
          });
        }
      );  
    }
  }

  render() {
    const title = <h2>Register User</h2>;
    const errors = this.state.errors;

    let alert = "";

    if(this.state.message){
      if(this.state.successful){
        alert = (
                  <Alert variant="success">
                    {this.state.message}
                  </Alert>
                );
      }else{
        alert = (
                  <Alert variant="danger">
                    {this.state.message}
                  </Alert>
                );
      }
    }

    return ( 
      <div>
        <AppNavbar/>
        <Container fluid>
          <Row>
          <Col sm="12" md={{ size: 4, offset: 4 }}>
          {title}
            <Form onSubmit={this.signUp}>
              <FormGroup controlId="forFirstname">
                <Label for="firstname">First Name</Label>
                <Input
                  type="text" 
                  placeholder="Enter First Name"
                  name="firstname" id="firstname"
                  value={this.state.firstname}
                  autoComplete="firstname"
                  onChange={this.changeHandler}
                />
                {
                  errors.firstname && ( 
                      <Alert variant="danger">
                        {errors.firstname}
                      </Alert>
                    )
                }
              </FormGroup>

              <FormGroup controlId="forLastname">
                <Label for="lastname">Last Name</Label>
                <Input
                  type="text" 
                  placeholder="Enter Last Name"
                  name="lastname" id="lastname"
                  value={this.state.lastname}
                  autoComplete="lastname"
                  onChange={this.changeHandler}
                />
                {
                  errors.lastname && ( 
                      <Alert variant="danger">
                        {errors.firstname}
                      </Alert>
                    )
                }
              </FormGroup>

              <FormGroup controlId="forUsername">
                <Label for="username">Username</Label>
                <Input
                  type="text" 
                  placeholder="Enter UserName"
                  name="username" id="username"
                  value={this.state.username}
                  autoComplete="username"
                  onChange={this.changeHandler}
                />
                {
                  errors.username && ( 
                      <Alert variant="danger">
                        {errors.username}
                      </Alert>
                    )
                }
              </FormGroup>

              <FormGroup controlId="formEmail">
                <Label for="email">Email</Label>
                <Input required
                  type="text" 
                  placeholder="Enter Email"
                  name="email" id="email"
                  value={this.state.email}
                  autoComplete="email"
                  onChange={this.changeHandler}
                />
                {
                  errors.email && ( 
                      <Alert variant="danger">
                        {errors.email}
                      </Alert>
                    )
                }
              </FormGroup>

              <FormGroup controlId="formPassword">
                <Label for="password">Password</Label>
                <Input required 
                  type="password" 
                  placeholder="Enter Password"
                  name="password" id="password"
                  value={this.state.password}
                  autoComplete="password"
                  onChange={this.changeHandler}
                />
                {
                  errors.password && ( 
                      <Alert key="errorspassword" variant="danger">
                        {errors.password}
                      </Alert>
                    )
                }
              </FormGroup>

              <Button variant="primary" type="submit">
                Create
              </Button>
              {
                !this.state.validForm && (
                  <Alert key="validForm" variant="danger">
                    Please check the inputs again!
                  </Alert>
                )
              }

              {alert}
            </Form>
            </Col>
          </Row>
        </Container>
      </div>);
  }
}

export default SignUp;

Implement Reactjs jwt Login component

reactjs jwt nodejs authentication wrong login user validation
reactjs jwt nodejs authentication wrong login user validation

Login component implement a function doLogin that uses a function signin() of AuthenticationService service to signin to secured Backend server with Jwt token.

import React, { Component } from 'react';
import AppNavbar from './AppNavbar';
import { Container } from 'reactstrap';
import { Form, Alert, FormGroup, Input, Label, Row, Col } from "reactstrap";
import {Button} from 'react-bootstrap';
import AuthenticationService from "../services/AuthenticationService";
import avatar from '../../avatar.png';

import '../../App.css';

class Login extends Component {

  constructor(props) {
    super(props);

    this.state = {
      username: "",
      password: "",
      error: ""
    };
  }

  changeHandler = (event) => {
    let nam = event.target.name;
    let val = event.target.value;
    this.setState({[nam]: val});
  }

  doLogin = async (event) => {
    event.preventDefault();

    AuthenticationService
        .signin(this.state.username, 
                  this.state.password)
      .then(
        () => {
          this.props.history.push('/profile');
        },
        error => {
          console.log("Login fail: error = { " + error.toString() + " }");
          this.setState({error: "Can not signin successfully ! Please check username/password again"});
        }
    );
  }

  render() {
    return ( 
      <div>
        <AppNavbar/>
        <Container fluid>
          <Row style={{marginTop:"20px"}}>
          <Col sm="12" md={{ size: 3, offset: 4 }}>
            <div style={{marginBottom: "10px"}}>
              <img src={avatar} alt="Avatar" className="avatar center" 
                style={{width: "50%", height: "auto"}}/>
            </div>
            <Form  onSubmit={this.doLogin}>
              <FormGroup>
                <Label for="username"><strong>Username</strong></Label>
                <Input autoFocus 
                  type="text"
                  name="username" id="username"
                  value={this.state.username}
                  placeholder="Enter Username"
                  autoComplete="username"
                  onChange={this.changeHandler}
                />
              </FormGroup>

              <FormGroup>
                <Label for="password"><strong>Password</strong></Label>
                <Input type="password" 
                  name="password" id="password"
                  value={this.state.password}
                  placeholder="Enter Password"
                  autoComplete="password"
                  onChange={this.changeHandler}
                />
              </FormGroup>

              <Button type="submit" variant="primary" size="lg" block>
                Sign In
              </Button>
              {
                this.state.error && (
                  <Alert color="danger">
                    {this.state.error}
                  </Alert>
                )
              }
            </Form>
            </Col>
          </Row>
        </Container>
      </div>);
  }
}

export default Login;

Implement Reactjs jwt Profile component

Profile component just uses a function componentDidMount() that calls a function getCurrentUser() of AuthenticationService service to get the current user login’s info and shows them on UI.

Profile page of Thomas user
Profile page of Thomas user
import React, { Component } from 'react';
import AppNavbar from './AppNavbar';
import { Link } from 'react-router-dom';
import { Button, Container } from 'reactstrap';
import { Alert } from "react-bootstrap"

import AuthenticationService from '../services/AuthenticationService';

class Profile extends Component {

  constructor(props) {
    super(props);
    this.state = {user: undefined};
  }

  componentDidMount() {
    const user = AuthenticationService.getCurrentUser();
    this.setState({user: user});
  }

  render() {
    let userInfo = "";
    const user = this.state.user;

    // login
    if (user && user.accessToken) {

      let roles = "";

      user.authorities.forEach(authority => {
        roles = roles + " " + authority.authority
      });

      userInfo = (
                <div style={{marginTop:"20px"}}>
                  <Alert variant="info">
                    <h2>User Info</h2>
                    <ul>
                      <li>Username: {user.username}</li>
                      <li>Access Token: {user.accessToken}</li>
                      <li>Authorities: {roles}</li>
                    </ul>
                  </Alert>
                </div>
              );
    } else { // not login
      userInfo = <div style={{marginTop:"20px"}}>
                    <Alert variant="primary">
                      <h2>Profile Component</h2>
                      <Button color="success"><Link to="/signin"><span style={{color:"white"}}>Login</span></Link></Button>
                    </Alert>
                  </div>
    }

    return (
      <div>
        <AppNavbar/>
        <Container fluid>
        {userInfo}
        </Container>
      </div>
    );
  }
}

export default Profile;

Implement Reactjs jwt token User Page

reactjs nodejs jwt authentication - user content info
reactjs nodejs jwt authentication – user content info

In the componentDidMount() function, the component UserPage uses the service BackendService to load user-content from secured backend-server and show it on UI.

import AppNavbar from './AppNavbar';
import React, { Component } from 'react';
import { Container } from 'reactstrap';
import BackendService from '../services/BackendService';
import { Alert } from "react-bootstrap"

class UserPage extends Component {
  constructor(props) {
    super(props);
    this.state={
      content: "",
      error: ""
    }
  }

  componentDidMount() {
    BackendService.getUserBoard()
      .then( response => {
        this.setState({content: response.data})
      } , error => {
        console.log(error);
        this.setState({
          error: error.toString()
        }); 
      });
  }

  render() {
    return (
      <div>
        <AppNavbar/>
        <Container fluid>
            {
              this.state.content ? (
                <div style={{marginTop: "20px"}}>
                  <Alert variant="info">
                    <h2>{this.state.content}</h2>
                  </Alert>
                </div>
              ) : (
                <div style={{marginTop: "20px"}}>
                  <Alert variant="danger">
                    {this.state.error}
                  </Alert>
                </div>
              )
            }
        </Container>
      </div>
    );
  }
}

export default UserPage;

Implement Reactjs jwt token PM Page

Reactjs JWT Authentication - PM Content
Reactjs JWT Authentication – PM Content

In the componentDidMount() function, the component ProjectManagerPage uses the service BackendService to load pm-content from secured backend-server and show it on UI.

import AppNavbar from './AppNavbar';
import React, { Component } from 'react';
import { Container } from 'reactstrap';
import { Alert } from "reactstrap";
import BackendService from '../services/BackendService';

class ProjectManagerPage extends Component {
  constructor(props) {
    super(props);
    this.state={
      content: "",
      error: ""
    }
  }

  componentDidMount() {
    BackendService.getPmBoard()
    .then( response => {
      this.setState({
        content: response.data
      })
    } , error => {
      console.log(error);
      this.setState({
        error: error.toString()
      }); 
    });
  }

  render() {
    return (
      <div>
        <AppNavbar/>
        <Container fluid>
          {
            this.state.content ? (
              <div style={{marginTop: "20px"}}>
                <Alert color="info">
                  <h2>{this.state.content}</h2>
                </Alert>
              </div>
            ) : (
              <div style={{marginTop: "20px"}}>
                <Alert color="danger">
                  {this.state.error}
                </Alert>
              </div>
            )
          }
        </Container>
      </div>
    );
  }
}

export default ProjectManagerPage;

Implement Reactjs jwt token Admin Page

Admin Content page
Admin Content page

In the componentDidMount() function, the component AdminPage uses the service BackendService to load admin-content from secured backend-server and show it on UI.


import AppNavbar from './AppNavbar';
import React, { Component } from 'react';
import { Container } from 'reactstrap';
import { Alert } from "reactstrap";
import BackendService from '../services/BackendService';

class AdminPage extends Component {
  constructor(props) {
    super(props);
    this.state={
      content: "",
      error: ""
    }
  }

  componentDidMount() {
    BackendService.getAdminBoard()
      .then( response => {
        this.setState({
          content: response.data
        })
      } , error => {
        console.log(error);
        this.setState({
          error: error.toString()
        }); 
      });    
  }

  render() {
    return (
      <div>
        <AppNavbar/>
        <Container fluid>
          {
            this.state.content ? (
              <div style={{marginTop: "20px"}}>
                <Alert variant="info">
                  <h2>{this.state.content}</h2>
                </Alert>
              </div>
            ) : (
              <div style={{marginTop: "20px"}}>
                <Alert variant="danger">
                  {this.state.error}
                </Alert>
              </div>
            )
          }
        </Container>
      </div>
    );
  }
}

export default AdminPage;

Define Reactjs Router

App.js uses React Router to navigate between components:

  • path “/” and “/home” is mapped with Home component
  • path “/profile” is mapped with Profile component
  • path “/user” is mapped with UserPage component
  • path “/pm” is mapped with ProjectManagerPage component
  • path “/admin” is mapped with Admin component
  • path “/signin” is mapped with Login component
  • path “/signup” is mapped with SignUp component
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import './App.css';
import Home from './app/components/Home';
import Profile from './app/components/Profile';
import UserPage from './app/components/UserPage';
import ProjectManagerPage from './app/components/ProjectManagerPage';
import SignUp from './app/components/SignUp';
import AdminPage from './app/components/AdminPage';
import Login from './app/components/Login';

class App extends Component {
  render() {
    return (
      <Router>
        <Switch>
          <Route path='/' exact={true} component={Home}/>
          <Route path='/home' exact={true} component={Home}/>
          <Route path='/profile' exact={true} component={Profile}/>
          <Route path='/user' exact={true} component={UserPage}/>
          <Route path='/pm' exact={true} component={ProjectManagerPage}/>
          <Route path='/admin' exact={true} component={AdminPage}/>
          <Route path='/signin' exact={true} component={Login}/>
          <Route path='/signup' exact={true} component={SignUp}/>  
        </Switch>
      </Router>
    )
  }
}

export default App;

Configure URL proxy request

To proxy from /api to http://localhost:8080/api, add a proxy setting to app/package.json.

"scripts": {...},
"proxy": "http://localhost:8080",

Integrative Testing – Reactjs JWT Authentication with Nodejs Security

– Start Backend Server at port 8080.
– Start Reactjs application with cmd: yarn start

Testcase 1 – Reactjs JWT SignUp Nodejs RestAPI

Reactjs jwt authentication - signup Jack with user role
Reactjs jwt authentication – signup Jack with user role

– Check MySQL database:

Check MySQL database after Jack User is registered
Check MySQL database after Jack User is registered

Testcase 2 – Reactjs JWT Login Nodejs RestAPI with user role

reactjs jwt nodejs authentication wrong login user validation
reactjs jwt nodejs authentication wrong login user validation

Then re-login with right username/password: jack-loizenai.com/123456789.

-> after signin successfully, Reactjs application will be redirected to Profile component:

React.js user profile info
React.js user profile info

– Get User content:

reactjs nodejs jwt authentication - user content info
reactjs nodejs jwt authentication – user content info

Testcase 3 – Reactjs JWT SignOut Nodejs RestAPI

To signout, just click to the signout link at the most right side of Navigation-Bar, after signout successfully, Reactjs application will be redirected to Home page.

Reactjs Home Page - signout returned page
Reactjs Home Page – signout returned page

Testcase 4 – Reactjs JWT SignIn Nodejs RestAPI with other roles

1. Register a Adam user, with PM_ROLE and USER_ROLE:

Reactjs jwt authentication - register a user successfully with 2 roles user and pm
Reactjs jwt authentication – register a user successfully with 2 roles user and pm

– Check MySQL:

Check MySQL database - add Adam user with PM roles
Check MySQL database – add Adam user with PM roles

– Adam logins successfully with below user login’s info:

Reactjs JWT Authentication - PM Content
Reactjs JWT Authentication – PM Content

Profile page of Adam user
Profile page of Adam user

2. Register Thomas user, with ADMIN_ROLE:

Reactjs JWT Authentication - register a customer with admin role
Reactjs JWT Authentication – register a customer with admin role

– Check MySQL:

Check MySQL database - add Thomas - admin user
Check MySQL database – add Thomas – admin user

– Access the admin-content:

Reactjs jwt authentication - admin page
Reactjs jwt authentication – admin page
Reactjs jwt authentication - sign in with a role admin
Reactjs jwt authentication – sign in with a role admin

Further Reading

Related posts:


– See LocalStorage object:

Localstorage store User's info
Localstorage store User’s info

Sourcecode

Full sourcecode: “Reactjs Jwt Nodejs/Express Token Authentication Example – React.js Spring Security Login”

1. Reactjs Frontend:

reactjs-jwt-token-authentication

Github sourceode:

reactjs-jwt-authentication-github

2. Jwt Nodejs/Express Token Authentication Backend:

jwt-springboot-reactjs-token-authentication-example

Github sourcode:

jwt-springboot-reactjs-jwt-authentication



By grokonez | February 1, 2021.


Related Posts


2 thoughts on “React Node Jwt Authentication without Redux – using LocalStorage and Axios”

Got Something To Say:

Your email address will not be published. Required fields are marked *

*