In this article, we’ll delve into how to implement JWT using NodeJS and why I think it’s the best way forward for REST architecture.

JWT stands for JSON Web Token and is a standard defined in RFC 7519 for “representing claims to be transferred between two parties.” The token content is written in JSON and added as a payload which is digitally signed for later verification. The reason we sign it is to ensure the integrity token and protect against the manipulation of the custom token claims and payload.


Node has a number of packages for making JWTs, and the one I’ll use here is jsonwebtoken. It’s installed with npm just like any other Node package, but we need to do one more thing before proceeding; generate a public/private RSA (min 2048 bits) key pair to be used when signing our key.

A token needs a payload, and for this example, we’ll use a payload that mimics a MongoDB document.

// index.js
const jwtHelper = require('./jwtHelper');

const user = {
  '_id': 1,
  'name': 'Martin Ricken',
  'roles': ['editor', 'developer', 'architect']
}

const token = jwtHelper.newToken(user);

To sign a token, we include the jsonwebtoken library and call the sign method. Here we pass in our payload, the private key, and any options.

// jwtHelper.js
const fs = require('fs');
const jwt = require('jsonwebtoken');

const jwtExpiresIn = 300;
const jwtPrivate = fs.readFileSync('./jwt', 'utf8').toString();
const jwtPublic = fs.readFileSync('./jwt.pem', 'utf8').toString();

const tokenPayload = (user) => {
  return {
    id: user._id,
    username: user.name,
    role: user.roles
  };
}

const newToken = (user) => {
  const payload = tokenPayload(user)
  const options = {
    expiresIn: jwtExpiresIn,
    algorithm: 'RS256',
    audience: 'json-web-token-example'
  };

  return jwt.sign(payload, jwtPrivate, options);
}

Now that we have the token let’s briefly cover how it should be exchanged with your API. The standard is to forward it with the headers, and for this reason, the JWT is base64url encoded, a form of encoding similar to base64 but specifically url safe.

The token should be included in the Authorization header - also known as a Bearer Token. The headers sent to your API should look something like this:

> GET /api/v1/users/1 HTTP/1.1
Host: localhost:3000
User-Agent: insomnia/2022.7.3
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYzZmI1NGZlZjllYzQwNDBlZDAzMzEwMiIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNjc3NDE4MzUxLCJleHAiOjE2ODUxOTQzNTF9.cUVU6YIzlUCij0QUnZnwNCFGsFTH4005lBIOFfnR8os
Accept: */*

As you can see from my User-Agent, I use the Insomnia REST application, an alternative to Postman. At a later date, I might go deeper into why, but for now, I’ll leave it up to you how you send requests to your API; just remember to add the Authorization header.


Once we receive the header with the token, we need to verify its validity. A token can be invalid for many reasons, and you should implement checks that correspond with your application. A few examples of things you should look at when deciding if a token is valid are:

  • Is the token expired? Tokens have a lifetime, so we pass in the option expiresIn, telling the library how long the token should be valid.

  • Is the token using a valid signature? If the token has been manipulated, the token signature will not match, and the token should be considered invalid.

  • Did the user change credentials recently? If so, this might indicate a security problem, and all tokens older than when the credentials were changed should be expired.

  • Does the user still exist, and is it active in your system? Many developers forget to check if a user is active, causing tokens to be valid even after being disabled. 

  • Has that been a forced expiration event? Sometimes you’ll want your applications to have the ability to expire all tokens. Things like payload or token lifetime changes and security issues could result in you needing to expire all tokens. So having that ability is worthwhile.


In this example, we’ll only cover token expiry and validity. The rest is too application dependent and would require a much longer example.

// jwtHelper.js
const { promisify } = require('util');

const verifyToken = async (token) => {
  const decoded = await promisify(jwt.verify)(
    token,
    jwtPublic
  );

  return decoded;
}

To verify a token, we call the verify method, which takes a token and the public key matching the private key used for signing the token. In the verifyToken method above, I’ve used the promisify utility to make the verification step asynchronous and made the method itself asynchronous. 

This is all to show how, but it’s worth noting that you should always consider running all Node asynchronously, as Node is single-threaded, and you don’t want to block the main thread.

Once verified, the decoded token will contain all my registered and payload claims. So I will have access to the id, username, and roles from my payload in the decoded object.


So now you know how to generate a JWT. So what payload do you put into it? The RFC specifies a few registered claims which can be used for various things, such as:

  • (iss) Multiple issuer environments can be used for logging where faulty keys were issued, so you can track down issues.

  • (sub) Subject can be used to identify key types, such as the difference between an authorization key and a data key used for transporting data between services.

  • (aud) Audience can be used to identify which services should accept the key. This can be very useful in a microservice setup if you want to limit access to certain services.

Personally, I’m a fan of using the payload itself to transport permissions and/or roles which the user has. In a microservice architecture, we could need to be able to verify if a user has permission to do certain actions, and by putting this information into the payload, we can verify without having to look up user permissions.

In the example, my payload includes a list of roles. I’ll use these roles in a permission check at my service endpoint security middleware to either deny or allow access.

The payload username and ID are useful for audit and error-logging purposes. You can put what you want in the payload, but keep in mind that if the token is compromised, you’re leaking information about the system. So be careful what you put in there, and weigh the usefulness vs. the risk of putting the information in the token.


JWTs are very useful, especially for decoupled software infrastructure architecture such as Microservices, Security First, or Serverless. As technology progresses and more APIs are built to scale, it becomes critical to think about how we deal with authorization, not just authentication.

Adding user roles to your JWT payload is one way of dealing with authorization that doesn’t break the decoupling principles. More importantly, it also allows us to inject user permissions into our API. This means that if certain parts of our REST API depend on permissions, we can inject these into it. That makes it a solution that allows us to adhere to the SOLID principles of software development at an infrastructure level.


Don’t forget to leave a follow on Medium or LinkedIn to see what else I get up to.