Authentication Middleware with JWT in Node.js

In any website or web application, ensuring that users are who they claim to be and protecting sensitive data from unauthorized access is crucial . One popular method for implementing authentication in Node.js is using JSON Web Tokens (JWT) and custom middleware. In this article, lets explore how to create a middleware that authenticates users using JWT.

What is JWT?

JWT (JSON Web Token) is an open standard for securely transmitting information between parties as a JSON object. This information is digitally signed, ensuring its integrity and authenticity. JWT is commonly used for authentication and authorization purposes.

A JWT is composed of three parts:

  • Header: Contains metadata about the token, such as the type of token (JWT) and the signing algorithm used (e.g., HMAC SHA256).
  • Payload: Contains the claims, which are statements about an entity (typically, the user) and additional data. There are three types of claims: registered, public, and private.
    • Registered claims: Predefined claims like iss (issuer), exp (expiration time), sub (subject), and aud (audience).
    • Public claims: Custom claims defined, such as user roles.
    • Private claims: Claims agreed upon between parties using the JWT.
  • Signature: Used to verify the token’s integrity and authenticity. It is created by encoding the header and payload using Base64Url, concatenating them with a dot, and signing the resulting string with a secret key using the specified algorithm.

A JWT typically looks like:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Setting Up the Project

Before we dive into the middleware, let’s set up a basic Node.js project with Express.

  • Initialize a new Node.js project:
mkdir auth-middleware
cd auth-middleware
npm init -y
  • Install necessary packages:
npm install express jsonwebtoken body-parser
  • Create a basic Express server:
// server.js
const express = require('express');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');

const app = express();
app.use(bodyParser.json());

const PORT = process.env.PORT || 3000;

app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

Creating the Middleware

Next lets create the middleware that will intercept and handle authentication in our application

  • Create the middleware:
// middleware/auth.js
const jwt = require('jsonwebtoken');

const authenticateToken = (req, res, next) => {
    const authHeader = req.headers['authorization'];
    const token = authHeader && authHeader.split(' ')[1];

    if (token == null) return res.sendStatus(401); // If no token, unauthorized

    jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
        if (err) return res.sendStatus(403); // If invalid token, forbidden
        req.user = user;
        next(); // Proceed to the next middleware or route handler
    });
};

module.exports = authenticateToken;
  • Protecting routes using the middleware:
// server.js
const express = require('express');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const authenticateToken = require('./middleware/auth');

const app = express();
app.use(bodyParser.json());

const PORT = process.env.PORT || 3000;

// Dummy user for demonstration
const user = { id: 1, username: 'testuser' };

// Route to login and generate a token
app.post('/login', (req, res) => {
    const username = req.body.username;

    if (username !== user.username) {
        return res.status(403).send('User not found');
    }

    const accessToken = jwt.sign(user, process.env.ACCESS_TOKEN_SECRET);
    res.json({ accessToken });
});

// A protected route
app.get('/protected', authenticateToken, (req, res) => {
    res.send('This is a protected route');
});

app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

Generating and Verifying JWTs

When a user logs in, for us to verify the user, we need to generate the JWT and verify that user is having a valid token

  • Generate a JWT:

In the /login route, we create a token using ‘jwt.sign()

const accessToken = jwt.sign(user, process.env.ACCESS_TOKEN_SECRET);
  • Verify a JWT:

In the middleware, we use ‘jwt.verify()‘ to check the token’s validity

jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
    if (err) return res.sendStatus(403);
    req.user = user;
    next();
});

Environment Variables

In order to keep our security keys for access token generation hidden from public , lets store our key in a .env file in the server and load that in our application when needed

Create a ‘.env‘ file in the project root:

ACCESS_TOKEN_SECRET=secret_key

Load these variables in the application using ‘dotenv‘:

npm install dotenv
// server.js
require('dotenv').config();

Conclusion

By implementing custom middleware with JWT, we can secure our Node.js applications with a robust authentication mechanism. This approach ensures that only authenticated users can access protected routes, enhancing the overall security of our application.

Building a Strategic Security Roadmap – What I learnt over the years…

Several years back I had my first stint at building a strategic security road map to a client I was working with. I must say I was not a practiced and seasoned expert who knew “a-z” of building security strategies and road maps. So as usual, I just rolled up my sleeves and started digging in to finding out all I can about what needs to be done, who are involved , what should be my approach and thousand other questions I had. This did lead me down many rabbit holes, and after several years of researching, discussing with experts, working with security architects and many implementations later, I have come a little bit further down the road of implementing security strategies and I assume I am bit wiser (so I would like to think).

In this blog series I will try to impart what I have learnt so far in my journey in helping multiple clients implement security road maps from undersetting the need, planning, documenting, getting the buy in, to implementing security road maps.

So the first thing I needed to understand was the big “WHY”

Why do we need implement a security strategy in the first place? my take on it and as I see it, was for any organization to ensure that their “valuables”, such as information technology or systems or data are kept safe from unwanted access and usage.

Well, what does that mean then ?

Well, any organization has things that they value , such as data, systems, devices, or any other components that has business value to the organization, which are called “Assets“. Through a weakness in the organizations environment called “Vulnerabilities“, assets can be exploited by intruders called “Threats“. Hence these are the “Risks” that any organization will face. When building our strategy we are suppose to identify these risks and come up with plans, designs, and actions to mitigate these risks. We have to do this by putting relevant controls in place

When defining risks and their corresponding controls, we will need to consider

  1. The opportunity for threats to exploit vulnerabilities with controls for mitigation.
  2. The chance that a vulnerability will lead to a compromise by using controls for detection.
  3. The effect that a compromise has, in terms of impact by using controls to response.

In order to implement a strategy we will need to come up with a strategy that allows an organization continuously look at,

  • Planning – studying and then designing a resistant security architecture for various IT projects in line with business needs and risk acceptance
  • Developing – prerequisites for networksfirewallsrouters, and other network devices
  • Performing – vulnerability assessmentsecurity testing, and risk analysis
  • Researching – the updated security standards, systems, regulatory frameworks and best practices
  • Communicating – relevant risks, vulnerabilities, and mitigation strategies to senior leadership

So how do we go about doing this…

Well, this is just the start on my approach to enable an organization to implement a security road map. In my next articles I dive a bit deeper, by taking a case study on an actual implementation of a strategic security roadmap, what was learnt, what went right , what failed, and some tips and tricks .