Our Work

Authentication using JWT from ReactJS Single Page Application (SPA) to NodeJS/Express

Updated 1 month and 15 days ago

In this tutorial we'll cover how to implement secure JWT authentication from ReactJS frontend with NodeJS/Express backend. As you are aware, JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties

Before we dig deeper, let's go through some of the use cases where this can be used and understand the structure of JSON web token.

Use Cases

Here are few use cases where JSON Web Tokens are useful:

  • Authorization: The most common use of Json web tokens is, it authorizes the initial request and generates a token and further requests are validated using the intial token generated till the token expires 
"jwtExpiry": "500m",//token expiry time set in config file in minutes
  • Information Exchange: Json web tokens are a best way to transfer data in web requests.They also make use of the public and private keys where the sender of the requests can be authenticated.It also provides a provision to make sure that the request has not been tempered in the middle.

High Level Flow Diagram

Structure of the JSON Web Token:

The Json web token consists of three parts separated by dots(.).They are

  • Header
  • Payload
  • Signature
Header

The header consists of two parts.

  1. The type of token which is being used which is jwt itself.
  2. The second is the algorithm that is being used to hash the data for the token
Payload

The payload consists of three claims

  1. Registered
  2. Public
  3. Private

The first two claims(registered and public) are not mandatory and can be specified at the will of the client.

The third claim(private):The private claim is created to share information between parties that agree on using them

Signature

The signature is used to verify that the message wasn't changed along the way, and, in the case of tokens signed with a private key, it can also verify that the sender of the JWT is who it says it is.

Typically the jwt token looks in the following format

xxxxx.yyyyy.zzzzz

  • Whenever a client request for login we authenticate the user with his credentials from the database.Then we generate a jwt token using the jwt library and send it back to the client.

With that background in our mind, let's start how will use this for our activity, which is authenticating from ReactJS.

Prerequisites

  • Install Node.js/Express
  • Install ReactJS

For installation of node.js and react in your application you can follow the link\

For the purpose of the article this tutorial we have broken down this authentication exercise into simple 6 steps.

Step 1

Let's define a secret key for our application in Node.Js/Express backend.

  • We configure a secret key in our configuration files in the following ways. You can named this file as config.js or config.json. Typically in your config folder path of node.js
"secret": "YXSkZmFzZmPhc2ZhZmFhc2RmYXNmMNGFmNDE1NDG0",//secret key in config file

Step 2

Lets Install the Jsonwebtoken package in NodeJS/ Express, which has the required methods for us to create tokens.

npm i jsonwebtoken

Step 3

In your NodeJS/ Express, Create a new controller in your controller folder or base path and add this authenticate method.

For our application we have created a controller folder and created a file userAuthenticate.js in it.

userAuthenticate.js
userRoutes.post('/authenticate', bouncer.block, function (req, res) {
const username = req.body.username
const password = req.body.password
if (_.isUndefined(username) || _.isUndefined(password)) {
return throwFailed(res, 'Authentication failed. User not found.');
}
User
.findOne({
where: {username: username},
})
.then(function (user) {
if (!user) {
return throwFailed(res, 'Authentication failed. User not found.');
}
bcrypt.compare(password, user.password, function (errBcrypt, resBcrypt) {
if (!resBcrypt) {
return throwFailed(res, 'Authentication failed. Wrong password.');
}
bouncer.reset(req);
return res.json({
token: generateToken(username, user.id),
});
});
});
});

Here we are verifying the username and passwords.

 bcrypt.compare(password, user.password, function (errBcrypt, resBcrypt) {
if (!resBcrypt) {
return throwFailed(res, 'Authentication failed. Wrong password.');
}

In this line the response containing the jwt token is sent back to the client(react) when the user passes the authentication by generating a new token. jwtwebtokens library is used here.

return res.json({
token: generateToken(username, user.id),
});

Step 4:

Now we will add code at the server.js file(node.js base patch). This listens for all api requests coming from the client and it also validates the tokens are coming from approved clients

Now we will add code at the server.js file(node.js base patch). This listens for all api requests coming from the client and it also validates the tokens are coming from approved clients… const tokenVerifier = require('../api/utils/token-verifier')//token verifier file

app.use('/api/users/, token-verifier, SettingRoutes)//a request coming from react users module to fetch data

For each request the application verifies the jwt token using the token-verfier.js file.

token-verfier.js
const jwt = require('jsonwebtoken')
const config = require('../config/config.json')
const User = require('../models/user')
module.exports = function (req, res, next) {
var token = req.body.token || req.query.token || req.headers['x-access-token']
if (!token) {
return res.status(403).send({
success: false,
message: 'No token provided'
})
}
jwt.verify(token, config.secret, function (err, decoded) {
if (err) {
return res.json({
success: false,
message: 'Failed to authenticate token'
})
}
let { id } = decoded
User.findOne({where: { id: id }})
.then(function (user) {
//console.log('user is this ',user)
if (user) {
req.user = user
next()
return
}
return res.json({
success: false,
message: 'No Such User'
})
}).catch((() => res.json({
success: false,
message: 'UnExpected error'
})))
})
}

The token verifier file takes the token from the request body of each request.

var token = req.body.token || req.query.token || req.headers['x-access-token']

Next we will verify the token with the secret key present in the config file which we have set up in step 1.

jwt.verify(token, config.secret, function (err, decoded) {//verifying with the secret key and give provision to client to continue further

 next()
Step 5

Now we will call our NodeJS/ Express methods which we created above from the client side i.e. ReactJS.

Create a file login-actions.js where you defined you login actions

Login-actions.js
 request
.post('authenticate', {//created in step 3
username,
password
}).then(response => {
if (_.isUndefined(response)) {
ReactGA.event({
category: 'Login',
action: 'Login Failed'
})
return dispatch(loginFailure('Error: No response'))
}
let { token } = response.data//retreiving the token sent from the node.js
if (token) {
ReactGA.event({
category: 'Login',
action: 'Logged Successfully'
})
localStorage.setItem('token', token)//storing the token in our localstorage for subsequent requests
dispatch(loginSuccess({ token }))
notification.success('Login successful')
Object.assign(request.defaults, {
headers: { 'x-access-token': localStorage.getItem('token') }//Here we are retreiving the access token in our localstorage
})
appHistory.push('/')//sending to the next route typically in all applications the Home Page
} else {
ReactGA.event({
category: 'Login',
action: 'Login Failed'
})
if (response.data.message) {
notification.error(response.data.message)
} else {
notification.success('Wrong username or password')
}
localStorage.removeItem('token')
}
}).catch(error => {
if (error) {
return dispatch(loginFailure(error))
}
})
}
}
Understading the Login-actions.js

The login-actions.js is the file that sends a request to node.js for authenticating the user and receives the response as a token.

 request
.post('authenticate', {//used in step 3
username,
password
})
 let { token } = response.data//retreiving the token sent from the node.js

This token is stored in the clients browser and used for subsequent requests

 localStorage.setItem('token', token) //storing the token in the localstorage
 localStorage.getItem('token') //retrieving the token from the local storage
Step 6

The next challenge is how to append the same jwt token to all subsequent requests. For this we will use package named axios.

Axios Package

npm i axios

Axios is Promise based HTTP client for the browser and node.js, it can be used by which Requests can be made by passing the relevant config . We create a new axios instance to append the token for every request form the client.

How do we use the axios

Consider we need to display the users grid in the front end.

For this we create an action file,you can name it user-actions.js file

user-actions.js
import request from 'api-request'//here we will use the axios package
export function getUsers() {
return async (dispatch) => {
try {
let res = await request.get('users')//send request to retreivie all users from node.js
if (res) {
// console.log('actions ', res.data)
dispatch(StoreUsersData(res.data))
}
} catch (e) {
console.log(e)
}
}
}
We have defined the axios package in the following path
/client/src/utils/api-request/index.js
let axios = require('axios')
let instance = axios.create({
baseURL: process.env.REACT_APP_API_URL ,
headers: { 'x-access-token': localStorage.getItem('token') }//here we add header to each request
})
module.exports = instance

In this way we can make secure authentication and transfer our data between the node.js and the reactjs.

Related Posts

Tags

    No tag results found for this post