Introduction to JSON Web Tokens

JSON Web Token (JWT) has become one of the most popular standards for secure authorization and information exchange on the internet. In this comprehensive guide, we will explore everything you need to know about JWTs – how they work, their structure, use cases, security considerations, and more. By the end, you’ll have all the knowledge required for implementing and consuming JWTs in your applications confidently. Let’s get started with Securing APIs with JSON Web Tokens!

In the world of web and mobile applications, there is often a need to securely transmit information between parties over the internet. Some common examples include:

  • Authentication and authorization.
  • Safely sending sensitive data between a client and a server.
  • Passing along user context in interactions with APIs.
  • Securely communicating with microservices architectures.

This is where JSON Web Tokens (JWT) come into play.

JWT is an open industry standard that defines a secure and compact way for transmitting information between two parties via a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a cryptographic algorithm to ensure that any changes made to the claims can be easily detected on the receiving end.

Basics of What Defines JSON Web Tokens

  • JSON-based: JWTs leverage the JSON data format to send and receive data in a human-readable way.
  • Self-Contained: The payload sent via JWT contains all the required information about the user, avoiding any need to query the database multiple times.
  • Digitally Signed: JWTs use cryptographic signing that ensures the integrity and authenticity of tokens.
  • Compact: JWT claims are encoded into a URL-safe string that is very compact compared to extensive XML- or JSON-based tokens.
  • Stateless: No server-side session state is required since all the necessary information is already contained within the token.

By using JWT authentication, applications can avoid Sessions that would previously store state. This results in improved performance, scaling, and user experience across modern web and mobile apps.

Next, let’s get into more depth on how these outstanding little JSON web tokens are structured and made.

How JSON Web Tokens Are Structured

JWTs consist of three main components that are base64 URL encoded and separated by a dot to form what we call the token itself:

  1. Header
  2. Payload
  3. Signature

Here is an example of how a JSON Web Token is structured:

aaaaa.bbbbb.ccccc

Let’s explore what each of these sections contains in more detail:

1. Header

The header commonly consists of two parts:

  • typ – Set to JWT
  • alg – The cryptographic algorithm, such as HS256 used to sign the token

An example header would look like:

{
"typ": "JWT",
"alg": "HS256"
}

Then, this JSON is base64url encoded to form the first part of the token.

2. Payload

The second part of the token is the payload, which contains the claims. Claims are statements about the user and additional metadata that serve as the guts of the token. There are three main types of claims:

  • Registered Claims – these are predefined claims that provide common useful information like token issuer, expiry etc. Some examples are iss for issuer, exp for expiration time, sub for subject.
  • Public Claims – these can define custom claims that contain any additional information you want to include in the payload.
  • Private Claims – claims that are agreed upon by parties sharing the tokens.

An example JWT payload could look like:

{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022
}

The payload shown above contains the following claims:

  • sub – The subject or user that this token refers to.
  • name – The name of the user.
  • admin – Boolean flag indicating user is an admin.
  • iat – Issued at time when token was generated.

Finally, the payload is also base64url encoded to form the second part of the JSON web token.

3. Signature

To create the signature part, we must create a string consisting of the base64url encoded header and payload separated by a dot and sign it with a secret key using the header algorithm, like this:

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

The signature ensures that the token hasn’t been altered in any way by an unauthorized party. As long as the signature is valid and the signing key remains hidden, we can trust that the JWT content hasn’t been tampered with. If someone changes the header or payload, the receiving party’s signature validation would fail.

That covers the basic JWT structure and components that make up JSON web tokens. Next, let’s see how these tokens are created and used for authentication purposes.

JWT Authentication Flow

JSON web tokens enable simple, secure authentication and authorization in applications and microservices via a standardized token format. Now let’s get into more detail on how the JWT flow works:

  1. A user signs up or logs into an application with their credentials.
  2. The application validates the credentials against a database or external provider like OAuth.
  3. If authentication succeeds, the application generates a JWT with user claims and a signature as we learned in the previous section.
  4. The client stores this JWT access token locally, often in local or session storage.
  5. For each subsequent request to restricted APIs and resources, the client includes the access token in the Authorization header.
  6. The API verifies the signature and expiry time to authenticate requests. If valid, the user can access permitted resources.
  7. Once expired, access tokens must be refreshed by re-authenticating the user. Refresh tokens are also employed to enable a seamless renewal of access tokens.

This allows a stateless authentication architecture to be enforced where the server does not have to maintain user sessions. All the user state and identification is passed along each transaction via JWT tokens instead.

Next, let’s go through some of the significant benefits this method of stateless authentication using JWT enables.

Why Use JSON Web Token Authentication?

JSON web tokens bring some major advantages compared to traditional session-based authentication:

Improved Performance & Scalability

Avoiding sessions allows easy horizontal scaling as multiple servers don’t need to sync session data. This is essential for building performance-intensive web and mobile applications.

Better User Experience

No more complicated cookies! Users can access pages seamlessly especially when switching domains as no session needs to be maintained.

Increased Security

Payloads are digitally signed and encrypted to mitigate tampering.

Compact

JWTs add very little overhead compared to large cookies required for session data serialization.

Mobile & Browser Friendly

JWT works across domains and supports modern mobile application auth better compared to sessions. Can be stored natively on mobile device or inside browser storage.

Control of Data Exposed

You have precise control over custom claims to be added to the JWT with needed user properties and access privileges.

We will cover some of these advantages in more detail through the rest of this guide.

These great features have resulted in JWT usage growing substantially over recent years, especially in cloud-native, microservices-based systems. Let’s now dive deeper into how we can create and work with JSON web tokens.

How to Creating and Validating JSON Web Tokens in Code

We’ve now got a grasp of JWT fundamentals. Let’s shift gears and see how to programmatically generate, decode, and validate JWT tokens using code. This will enable putting JWTs into action securely.

Generating JWTs

Most web frameworks and libraries provide JWT creation utilities that handle most of the complexity behind the scenes. But it still helps greatly to understand what’s happening under the hood.

Let’s see how to generate JWTs in JavaScript and Node.js using the popular jsonwebtoken library.

Firstly, we need to install jsonwebtoken:

npm install jsonwebtoken

To create a signed token, call jwt.sign():

const jwt = require('jsonwebtoken');
const payload = {
userId: '1234',
username: 'tom',
isAdmin: true
};
const secretKey = 'super-secret!';
const token = jwt.sign(payload, secretKey, {algorithm: 'HS256'});
console.log(token);
// eyXXX.yyyyy.zzzzz

The sign method needs the following parameters:

  • Payload object
  • Secret signing key
  • Options with algorithm such as HS256

This will generate the complete signed JSON web token that looks something like:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjM0IiwidXNlcm5hbWUiOiJ0b20iLCJpc0FkbWluIjp0cnVlfQ.AgxpRmtu8e-QpFTlDlxMTScCPlJ9l4mnM6cGitEE0lYg

The receiving application having the secret key can then verify the signature to trust the token contents haven’t been altered by anyone without the key.

Let’s see how token validation works next.

Validating JWTs

On the server side, JWTs needs to be properly validated before trusting their contents. This involves:

  • Checking signature validity using the secret
  • Ensuring the token is not expired to mitigate replay attacks
  • Verifying required claims like issuer, audience

Here is an example using jsonwebtoken:

const jwt = require('jsonwebtoken');
// Get token from auth header
const token = req.header('Auth');
// Verify JWT
try {
const decoded = jwt.verify(token, secretKey);
console.log(decoded);
} catch(err) {
// Invalid token
throw new Error('Authentication invalid');
}

This jwt.verify() method will take care of validating the signature and other criteria like expiry timestamps.

If the checks pass, we get the decoded payload containing all our custom claims about the signed user. Now the server can identify requests coming from authenticated users confidently.

Asymmetric Public/Private Keys

For further security, applications can use public/private keypairs instead of secret keys to sign the tokens. This ensures only the issuing server having the private key can generate tokens, while public key is used by receiving services for verification.

Let’s also take a quick look at how we can inspect JWT contents for debugging needs.

Inspecting JSON Web Token Claims

While integrating JWT authentication in apps, it really helps to visualize and validate what’s inside the tokens as we build out various flows. Let’s go through a few handy ways to decode, inspect, and debug JSON web tokens.

Decoding Raw JWT

The raw JWT is just a string concatenation of base64 encoded header, payload and signature. We can split these apart and decode each section to peek inside using code like:

const token = 'xxxxx.yyyyy.zzzzz';
const [header, payload, signature] = token.split('.');
const decodedHeader = JSON.parse(Buffer.from(header, 'base64').toString());
const decodedPayload = JSON.parse(Buffer.from(payload, 'base64').toString());
console.log(decodedPayload);

This lets us access all the standard registered claims as well as any custom claims we may have added for the user.

Using jwt.io

JSON web tokens are meant to be compact, but also human-readable where needed for troubleshooting.

One excellent resource available is jwt.io. This site allows pasting any JWT token and instantly shows decoded header and payload in a browser-friendly format. It also lets you generate sample tokens and assists in validating signature issues quickly.

Other similar tools (for JWT Token Decode) like jsonwebtoken.io provide a fast way to decode JWTs during development.

These online token decoders prove invaluable when working with JWT authentication.

Custom Decoding Methods

For added flexibility, we can create custom functions to retrieve specific information from tokens programmatically:

// Helper method to parse username from token
function getUsername(token) {
const payload = decodeToken(token);
return payload.username;
}
console.log(getUsername(token)); // tom

This allows reusable extraction of any details we need from JSON web tokens.

With that, we’ve covered a wide range of fundamentals from JWT internals, usage in authentication, gotchas to look out for and even decoding techniques.

Now we’re ready to jump into implementing full-fledged JWT authentication and authorization in our apps!

Adding JWT Authentication to Node.js APIs

To give us a more concrete example of applying everything so far, let’s walk through adding end-to-end JWT authentication to secure APIs and resources in a sample Node.js application.

We will:

  1. Set up user signup/login flow
  2. Generate JWTs after successful authentication
  3. Require valid JWTs for accessing protected routes

Project Setup

Let’s initialize a new Node project:

npm init

Install dependencies:

npm install express jsonwebtoken

We are all set to write some code!

User Authentication

First, we need some way to register and authenticate users with a username and password before providing access.

For simplicity, we will store user records in a local in-memory array:

// Dummy user DB
const users = [];
function signup(username, password) {
// Create new user
const user = {
username,
password // Store hashed passwords for real apps!
};
users.push(user);
}
function login(username, password) {
// Find user by username
const user = users.find(u => u.username === username);
// Validate password
if(!user || user.password !== password)
throw 'Invalid credentials!';
return user;
}

This gives us basic mock authentication to test out JWT workflow.

Generating JWTs

Next, we define utility functions for generating signed JWT tokens upon login and parsing them from requests later:

// Secret key for signing tokens
const secretKey = 'jsdkfjoi3j3kmd';
// Generate JWT
function generateToken(user) {
const token = jwt.sign({
userId: user.id,
username: user.username
}, secretKey, { expiresIn: '1h' });
return token;
}
// Extract JWT from request
function getToken(req) {
const authHeader = req.headers['authorization'];
const token = authHeader.split(' ')[1];
return token;
}

We are passing the user id and username as custom claims into the payload during token creation.

The tokens will be set to automatically expire after 1 hour here. Short-lived tokens improve security.

Protecting Routes

Finally, we need middleware that will validate the JWT from incoming requests before allowing access to actual application logic:

function authenticate(req, res, next) {
const token = getToken(req);
try {
const decoded = jwt.verify(token, secretKey);
// Access grants
req.user = decoded;
next();
} catch {
// Invalid token
return res.status(401).send('Authentication failed');
}
}

We can now simply add this auth middleware to any routes we need protected:

app.get('/dashboard', authenticate, (req, res) => {
res.send('Welcome ' + req.user.username);
});

And that’s it! Our APIs now leverage the power of JSON web tokens to authorize requests.

This was just a brief sample to illustrate core JWT workflows for authentication and access control. Many additional checks would be needed for robust implementations.

There are also a variety of great libraries like Passport.js that provide out-of-box JWT integration with Express backends.

Next up, let’s explore refreshing JSON web tokens to keep applications secure but also avoid interruptions for users.

Refreshing JSON Web Tokens

Since JWTs generally expire after a short period for improved security, what happens when a token gets invalidated but user activity needs to continue? This brings us to token refreshing.

The idea is simple:

  • User tries to access resource, but token has expired.
  • Instead of directly prompting relogin, check for existence of a refresh token.
  • If refresh token is valid, issue a fresh access token so user can resume seamlessly.
  • Repeat when new access token also expires.

Benefits of introducing refresh tokens:

  • No reentering credentials each session
  • Long lived sessions through auto refresh capability
  • Better security via multiple tokens with shorter lifetimes

The refresh tokens must be stored securely to prevent unauthorized refresh granting.

Common JWT Vulnerabilities & Risks

While JWT provides good stateless authorization, some common vulnerabilities include:

Sensitive Data Exposure

Avoid storing sensitive data like passwords in JWT as payloads can be easily decode JWT Token.

JWT Tampering

Additional signature validation is must to prevent clients altering JWT claims.

Token Sidejacking

TLS connections prevent MITM sniffing attacks that steal JWT in transit.

Exposed Secret Keys

Secret keys if exposed enable generating bogus signed JWTs allowing attackers access to APIs.

JWT Replay

A token replay attack retries old valid tokens to gain improper access unless TLS + expiry checks added.

Thus, care should be taken to use short lived signed JWTs over secure channels along with code free of security antipatterns.

Concluding Thoughts on Securing APIs with JSON Web Tokens

JSON Web Tokens have become an industry standard for stateless authentication due to their compact and self-contained nature. This article provided a comprehensive overview of what JWTs are, how they work, their structure, use cases and integration examples.

We covered the basics of JWTs including the header, payload and signature components that make up a token. We then explored JSON web token authentication flows for securely transmitting user identity across clients and servers. Example code snippets were provided for generating, validating, and refreshing JWTs in Node.js and JWT authentication spring boot applications to protect APIs and resources. While JWTs have some vulnerabilities to be aware of like token leakage and replay attacks, following security best practices can mitigate the risks substantially.

Amelie Lamb

Amelie Lamb

Amelie Lamb is an experienced technical content writer at SoftwareStack.co who specializes in distilling complex software topics into clear, concise explanations. She has a talent for taking dense technical jargon and making it engaging and understandable for readers through her informative, lively writing style.

Table of Contents