How ToT2 Dark_1200x303

JWTs offer a way to manage security concerns. In this post, learn how to generate a JWT in four steps.

Web servers need to manage security concerns by possessing the capability to grant or deny access to any entity on its resources.

This can be done using several methods, one of which is sessions, where the server uses a session id, which is attached to a cookie in a request to authenticate a user. Sessions work well with a few issues, such as the need to persist session ids to the database.

Another popular option we will explore and implement in this article is with JSON Web Token (JWT). For more information on JWTs, check out my previous post here.

Without further ado, let’s begin with generating a JSON Web Token using Node.js.

Prerequisites

To get the best out of this article, I assume you have basic familiarity with the following:

  • Node.js
  • Express

How To Generate a JWT

In this section, we will learn how JWTs are created from scratch.

Note: Other third-party packages exist that abstract the complexities of creating JWTs, but we will be implementing it from scratch for the sake of clarity.

Workspace Setup

Open your terminal and insert the following:

mkdir node-jwt
cd node-jwt
touch index.js

The above code creates a directory called node-jwt, then creates a file called index.js inside the directory.

Generating a JWT involves doing the following:

  1. Generating a Base64 encoded header
  2. Generating a Base64 encoded payload
  3. Generating a signature using a secret and the results in Steps 1 and 2
  4. Combining the results generated in Steps 1, 2 and 3

Before we go through each step above, insert the following helper methods in your index.js file to aid us in achieving the following:

  1. Convert a string to Base64:
const toBase64 = obj => {
   // converts the obj to a string
   const str = JSON.stringify (obj);
   // returns string converted to base64
   return Buffer.from(str).toString ('base64');
};

Here, we define a function that expects an object. It’s first converted to a string, then to a buffer encoded to Base64 and returned.

  1. Replace special symbols in a Base64 string:
const replaceSpecialChars = b64string => {
// create a regex to match any of the characters =,+ or / and replace them with their // substitutes
  return b64string.replace (/[=+/]/g, charToBeReplaced => {
    switch (charToBeReplaced) {
      case '=':
        return '';
      case '+':
        return '-';
      case '/':
        return '_';
    }
  });
};

You might be wondering why we need another method to replace certain symbols in a Base64 string. The reason is Base64 Strings contain symbols that are misinterpreted when used in a URL.

That is, these symbols are not URL friendly when working with JWTs. Below is a table listing all of them and their replacements for more clarity.

Character Replacement
= “”
+ -
/ _

Step 1: Generating the Header

Next, proceed by creating a sample header for the token using an object.

// suppose we have this header
const header = {
  alg: 'HS256',
  typ: 'JWT',
};
const b64Header = toBase64 (header);
const jwtB64Header = replaceSpecialChars(b64Header);
console.log ("the header is: ",jwtB64Header); 
//OUTPUTS the header is: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Not a lot going on here. We started by creating a sample object to be the header of a JWT and encoded it to a Base64 URL.

Step 2: Generating the Payload

Like the header, we create a token’s payload from an object.

// a sample payload 
const payload = {
  iss: 'a_random_server_name',//information about the server that issued the token
  exp: 872990,// tokens expiry date in milliseconds
  // information about some random user
  name: 'John Bobo',
  email: 'myemail@test.com',
  isHuman: true,
};
// converts payload to base64
const b64Payload = toBase64 (payload);
const jwtB64Payload = replaceSpecialChars (b64Payload);
console.log ("the payload is: ",jwtB64Payload);
//OUTPUTS the payload is:     eyJpc3MiOiJhX3JhbmRvbV9zZXJ2ZXJfbmFtZSIsImV4cCI6MTUwMCwibmFtZSI6IkpvaG4gQm9ibyIsImVtYWlsIjoibXllbWFpbEB0ZXN0LmNvbSIsImlzSHVtYW4iOnRydWV9

Similarly, we created and encoded an object representing the token to a Base64 URL.

Step 3: Generating the Signature

The signature is what makes a JWT trustworthy. To generate one, we make use of one of Node’s core modules called crypto. The crypto module provides cryptographic functionality that includes a set of wrappers for OpenSSL’s hash, HMAC, cipher, decipher, sign and verify functions.

Add the following to your index.js file:

// bring in the crypto module
const crypto = require ('crypto');
const createSignature =(jwtB64Header,jwtB64Payload,secret)=>{
// create a HMAC(hash based message authentication code) using sha256 hashing alg
    let siginature = crypto.createHmac ('sha256', secret);

// use the update method to hash a string formed from our jwtB64Header a period and 
//jwtB64Payload 
    signature.update (jwtB64Header + '.' + jwtB64Payload);

//signature needs to be converted to base64 to make it usable
    signature = siginature.digest ('base64');

//of course we need to clean the base64 string of URL special characters
    signature = replaceSpecialChars (signature);
    return signature
}
// create your secret to sign the token
const secret = 'super_secret_society';
const signature= createSignature(jwtB64Header,jwtB64Payload,secret);
console.log ("the signature is: ",signature);
//OUTPUTS the signature is Op-NH-zTetL6deCmSzyTO-f0jvVS4U7JGUG8ZryvsWE

We defined a function that creates a signature by concatenating the Base64 encoded header, a period and the payload gotten from Steps 1 and 2 above. Then, using a secret, the function generates a Hash Message Authentication Code (HMAC) using a hashing algorithm (sha256 in this case), which is then encoded to a Base64 URL and is returned.

Step 4: Combining the Results in Steps 1, 2 and 3

//we now combine the results of the header,payload and signatue
const jsonWebToken = jwtB64Header + '.' + jwtB64Payload + '.' + signature;
console.log ("the JWT is :",jsonWebToken);

//OUTPUTS:"the JWT is :"        eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhX3JhbmRvbV9zZXJ2ZXJfbmFtZSIsImV4cCI6MTUwMCwibmFtZSI6IkpvaG4gQm9ibyIsImVtYWlsIjoibXllbWFpbEB0ZXN0LmNvbSIsImlzSHVtYW4iOnRydWV9.Op-NH-zTetL6deCmSzyTO-f0jvVS4U7JGUG8ZryvsWE

Running the Code

Save the index.js file and open your terminal and execute the following:

node index.js 

Terminal tab shows code output, including header, payload, signature and JWT

Recommendations When Working With Tokens

  • Ensure that the secret you use to sign your tokens is strong and unguessable.
  • Verify the signature of a token on the server before accessing its payload.
  • Do not decode your tokens on the client side.
  • Do not store your tokens in local storage because it could make them vulnerable to attacks such as cross-site scripting (XSS).
  • Store your tokens in HTTP Only cookies.
  • Keep the payload of your tokens relatively small, i.e. your token should not be over-bloated.

Conclusion

I hope you had fun learning about JWTs and all the interesting stuff surrounding their structure and how they are implemented. Although JWTs provide a flexible means to authenticate users, it still suffers from different types of attacks such as CSRF and XSS if not used correctly. A clear understanding of this guide gives you an edge to start using and exploring them in depth in your future projects.


Chinedu
About the Author

Chinedu Imoh

Chinedu is a tech enthusiast focused on full-stack JavaScript and Infrastructure engineering.

Related Posts

Comments

Comments are disabled in preview mode.