top of page

How To Secure Your Node.js Application With JSON Web Token

Updated: Oct 26, 2023

When you build a web application where your front-end and back-end is separated, one way of putting it behind a login is with JSONWebToken. It is a concept that became popular very quickly when it was introduced in the early 2010s. In this post, you’ll learn what JSON Web Token (JWT) is, how it works and how to integrate it in your Node.js application. Let’s get started!


JSON Web Token in a nutshell

Quoting the official website, “JSON Web Token is an open, industry-standard method for representing claims securely between two parties“. Which means, a server can determine whether a piece of information (in JSON format) sent by the client has not been modified and has effectively been issued by said server.


What does a token include?

A JSON Web Token is composed of three parts

  • Header: Contains extra information about what kind of token it is (JWT) and which signing algorithm is being used (e.g. SHA256). The header JSON is being Base64Url encoded.

  • Payload: Contains information (or “claims”) that the two parties want to share. This could include anything you want, but it’s never a good idea to share sensitive data (such as passwords), because, by default, a JWT token can be decoded without a shared secret. JWT does not have the goal to encrypt the data. I personally usually use user ID, role, issue date, and expiration date. As well as the header JSON, the payload JSON is also encoded with Base64Url.

  • Signature: The signature contains the encoded header, encoded payload, a secret (that only your server knows) and is signed by the algorithm that’s determined in the header.

Were the hashing algorithm to be SHA256, the signature would be created as such:

HMACSHA256(
base64UrlEncode(header)
+ "."
+ base64UrlEncode(payload)
,secret)

In the end, all three parts are just being concatenated, separated by a “.”:

<Header>.<Payload>.<Signature>

Here an example JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1ZDM5YzI4MjdhZDgyMjBmMTJiMGZkMWIiLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE1NjUyNzE4NzUsImV4cCI6MTU2NjQ4MTQ3NX0.NmcvfTBmUw1cdEI1cNRHzq6q5W0HmPELoh8rd_7EFAc


Take a moment to head over to jwt.io and paste the token in there. You’ll see all the information that it contains (except the signature), or see the screenshot below –


How does a JWT token work?

It is important to notice that JWT is not meant to exchange encrypted data. It should never contain sensitive information such as passwords. The key in JWT lays in the verification of tokens. When you try to verify a token which has been tampered with (maybe a user ID has been swapped), the token will be rejected. Why? Because the content does not match up with the signature anymore. So a valid token cannot be created by somebody other than you, except when they get the hands-on your secret you use to hash the signature with.


In case your JWT secret gets hacked for some reason, you need to change it immediately. All already existing tokens from then on will be invalid. Which might be a little annoying for some logged-in users, but you can make sure that nobody can generate a valid token for your application.


How does a JWT workflow look like on a server?


JWT Workflow Between Client And Server


Now that we are a little familiar with JWT in general, let’s take a look at an example of how it would work with a client-server exchange.

  1. The first move makes the client. This could be a web frontend application, a mobile app, etc. Basically, anything that tries to interact with your backend application (for example a REST API). It sends their login credentials to the server for it to become verified.

  2. When the server receives the login request, it first makes sure that the username/email and password match up with information stored in the database (which hopefully has only encrypted passwords stored ). When the credentials are correct, this means for the server that this user is who he says he is.

  3. So a JWT token gets generated. Here, information that’s important for identifying the user is being passed into the payload. It’s also a good idea to include issue and expiration dates. So a session would never be longer valid than the time you indicate. One week seems like a good time span. The user should log out of the application after each use anyways, right? But this just adds one extra piece of security by avoiding zombie logged-in users.

  4. This token then is returned to the client as a response to his login attempt. When he receives a token, that means for him the login has been successful. The token should be stored somewhere locally on the client-side. This can be localStore for web applications or somewhere in a device variable for mobile applications.

  5. For all further communication with the server, the client adds an Authentication header to each request. This looks as such: Authentication: Bearer <token>

  6. When a new request to a protected resource arrives at the server, the first thing it does is to check if an Authentication header is passed along with the request. Is this the case, it tries to verify if the token checks out. If it’s not a valid token (it has been tampered with, it has expired, etc.), the request should be denied immediately.

  7. If the token is valid however, it’s safe to assume for the server that the user is still who he says he is and can return the requested resource as a response to the client.


JWT in a Node.js application

In this post, I’m not going into details about how web servers work in Node.js. But I’ll show you how you can use JWT in a JavaScript server environment.


Preparation

In order to work with JWT, you can use the handy jsonwebtoken library. Install it as such:

npm install jsonwebtoken


Create a token

At the place in your code where you determine if the client has provided correct login credentials (probably just after you checked the database), you can create the JSON Web Token:

const jwt = require('jsonwebtoken')
const token = jwt.sign(
    <Your payload>, 
    <Your JWT secret>, 
    { 
        expiresIn: <Expiration Time> 
     }
 )

In a real example, it could look like this:

const jwt = require('jsonwebtoken')
const token = jwt.sign(
{ 
    _id: user._id, 
    admin: true 
}, 
    process.env.JWT_SECRET, 
    { 
        expiresIn: '1 week' 
}
)

Notice two things:

  • If you are not familiar with dotenv, process.env.JWT_SECRET is where your JWT secret would be placed. It is never a good idea to store your token as clear text in your code, that’s why it’s a good idea to use tools such as dotenv to locate it in a file that’s not going to be uploaded to your Git repository.

  • The expiredIn property can be human-readable time indications in string form:

    • ‘4 days’

    • ‘7 hours’

    • ‘2 weeks’

    • ‘6 months’

    • etc.


Verify token

Your client should set the Authentication header as such: Bearer: <token>. Therefore you first need to strip the “Bearer: ” part away from the string:

const jwt = require('jsonwebtoken')
const token = req.header('Authorization').replace('Bearer ', '')

(req.header(‘Authorization’) is the Express.js way to read the authorization header)


Then, you can verify the provided token as such:

const jwt = require('jsonwebtoken')
try{
    const payload = jwt.verify(token, process.env.JWT_SECRET) 
    console.log(payload._id)
} 
catch(error) {
    console.error(error.message)
}

If the token is valid, you’ll have access to all the payload data right in the payload variable. If the token is invalid, the jwt library will throw an error you can treat in the catch.


Summary

That’s it! As you see it’s not that complicated to use JWT. The most important thing -and I can’t stress this enough- is that JWT is NOT encrypting your data, therefore DON’T use it to exchange sensible information.


JSON Web Token is an excellent technology to verify whether the information someone claims they have are actually authentic.


Source: codewall


The Tech Platform

0 comments
bottom of page