[Best-Practices] Securing NodeJS Express APIs with JWT Authentication and custom Authorization

NodeJS
November 08, 20222 minutesuserSmit Gajera
[Best-Practices] Securing NodeJS Express APIs with JWT Authentication and custom Authorization

Overview


A Node.js library for use as Express middleware to secure endpoints with JWTs. The implementation uses a JWT endpoint of an Authorization Server to get the keys required for verification of the token signature. There is also an example Express app that shows how to use the library.

Package: https://www.npmjs.com/package/jsonwebtoken

Using the JSON web token, we can simply authenticate each and every request on our server. As a standard / best practice, we can use JWT (JSON web token) middleware to validate all requests.

JWT Middleware


  const jwt = require('jsonwebtoken')

  module.exports = (expectedRole) => (req, res, next) => {

  const authHeader = req.get('Authorization')
  if (!authHeader) {
    const error = new Error('Not authenticated.')
    error.statusCode = 401
    throw error
  }

  const token = authHeader.split(' ')[1]
  if (!token) {
    const error = new Error('Not authenticated.')
    error.statusCode = 401
    throw error
  }

  let decodedToken
  try {
    decodedToken = jwt.verify(token, process.env.SECRET_KEY)
  } catch (error) {
    error.statusCode = 401
    throw error
  }

  if (!decodedToken) {
    const error = new Error('Not authenticated.')
    error.statusCode = 401
    throw error
  }

  const role = decodedToken.role

  const authorised = expectedRole.includes(role)
  if (!authorised) {
    const error = new Error('Not authorised.')
    error.statusCode = 401
    throw error
  }

  req.user = decodedToken
  next()
}

This middleware has been prepared and exported. Therefore, we need to include it in our routes file and pass it to the expected role, so in our JWT middleware, we will validate the request with the JWT token, then verify that the user has access to an expected role (this role saved in the database) to this endpoint.

Routes File


 const express = require('express')
 const router = express.Router()

 const auth = require('./auth/index')
 const admin = require('./admin/index')
 const common = require('./common/index')
 const authorize = require('../middleware/jwtAuth')

 router.use('/auth', auth)
 router.use('/admin', authorize(['admin']), admin)
 router.use('/common', authorize(['admin', 'user']), common)

 module.exports = router 

Now that we have set up our authentication and authorization middleware in our routes, we are passing the required role to access these routes. These roles will be checked against our user role.

Our middleware simply next() the request if the user has a valid JWT token and is authorized to access this route, otherwise, it will throw the global error that is caught by the express global error handler.