Understanding JSON Web Tokens (JWT): Uses, Structure, and Implementation

1785335042225-uif9vw.png

In modern web development, secure and scalable authentication is a top priority. Traditional session-based authentication often requires web servers to store session state in memory or databases, creating bottlenecks in distributed architectures.

JSON Web Token (JWT), defined in RFC 7519, provides a compact, URL-safe, and self-contained standard for securely transmitting information between two parties as a JSON object.

This guide covers what JWTs are, why they are widely used, their internal structure, code implementation examples, security best practices, and how to use an online tool to test and generate JWTs.


1. What is a JWT and What is it Used For?

A JSON Web Token (JWT) is an open standard that enables two systems to exchange data securely. Because JWTs are digitally signed (using a secret key via HMAC or a public/private key pair via RSA or ECDSA), the receiver can verify the authenticity and integrity of the contained claims.

Common Use Cases for JWT

  1. Stateless User Authentication:
    Once a user logs in, the authentication server issues a JWT. The client includes this token in the header of subsequent HTTP requests. The backend server verifies the token cryptographically without having to query a database or session store, making JWT ideal for stateless microservices and RESTful APIs.

  2. Information Exchange:
    JWTs are a reliable way of transmitting information between parties. Since JWTs can be signed, you can be sure the senders are who they say they are. Additionally, the signature certifies that the content hasn't been tampered with.

  3. Single Sign-On (SSO):
    JWT is widely adopted in SSO implementations (such as OAuth 2.0 and OpenID Connect). A single identity provider can issue tokens that multiple independent applications and services accept seamlessly.


2. Structure and Format of a JWT

A JWT consists of three distinct parts separated by dots (.):

$$ ext{JWT} = ext{Header} . ext{Payload} . ext{Signature}$$

In its string representation, a JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Let me break down each of the three sections:

+-------------------------------------------------------------------------+
|                               JWT FORMAT                                |
+-------------------------------------------------------------------------+
|  Header (Base64URL)    .  Payload (Base64URL)   . Signature            |
|  {"alg": "HS256", ...} .  {"sub": "1234", ...}  . HMACSHA256(...)      |
+-------------------------------------------------------------------------+

A. Header

The header typically consists of two parts: the token type (which is JWT) and the signing algorithm being used (e.g., HS256, RS256, ES256).

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

This JSON object is then Base64URL encoded to form the first part of the JWT.

B. Payload

The payload contains the claims—statements about an entity (typically, the user) and additional data. Claims fall into three categories:

  1. Registered Claims: Pre-defined, standard claims recommended for interoperability:

    • iss (Issuer): Who created the token.
    • sub (Subject): The subject/user ID of the token.
    • aud (Audience): The intended recipient of the token.
    • exp (Expiration Time): Timestamp (in seconds) after which the token expires.
    • nbf (Not Before): Timestamp before which the token must not be accepted.
    • iat (Issued At): Timestamp when the token was created.
    • jti (JWT ID): Unique identifier for preventing replay attacks.
  2. Public Claims: Custom claims defined by developers, which should be collision-resistant (or registered in the IANA JSON Web Token Registry).

  3. Private Claims: Custom claims created to share information between parties that agree on using them (e.g., user roles, email address).

Example Payload:

{
  "sub": "usr_987654321",
  "name": "Jane Doe",
  "role": "administrator",
  "email": "jane@example.com",
  "iat": 1700000000,
  "exp": 1700003600
}

This JSON object is Base64URL encoded to form the second part of the JWT.

Important Note: Base64URL encoding is NOT encryption. Anyone who intercepts a JWT can easily decode the Header and Payload to read their contents. Never store sensitive credentials (like passwords or payment details) inside an unencrypted JWT payload.

C. Signature

To create the signature part, you take the encoded header, the encoded payload, a secret key (or private key), and the algorithm specified in the header, and sign them.

For example, using the HMAC SHA256 algorithm, the signature is generated like this:

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

The signature ensures that the token hasn't been altered during transit. If any part of the header or payload changes, the signature validation on the server will fail.


3. How JWT Authentication Flow Works

+--------+                                                    +--------------------+
| Client |                                                    | Application Server |
+---+----+                                                    +---------+----------+
    |                                                                   |
    | 1. POST /login (username & password) ---------------------------> |
    |                                                                   |
    |                                                                   | [ Verify Credentials ]
    |                                                                   | [ Generate JWT signed with secret ]
    |                                                                   |
    | <--------------------------- 2. Return JWT Token ---------------- |
    |                                                                   |
    | [ Store token in HttpOnly Cookie or Secure Storage ]             |
    |                                                                   |
    | 3. GET /api/protected (Header: Authorization: Bearer <token>) --> |
    |                                                                   |
    |                                                                   | [ Validate Signature & Expiration ]
    |                                                                   | [ Grant access to resource ]
    |                                                                   |
    | <--------------------------- 4. Return Protected Data ------------ |
    |                                                                   |

4. Code Implementation Example

Here is a practical, concise example using Node.js with Express and the popular jsonwebtoken library to issue and verify JWTs.

Prerequisites

npm install express jsonwebtoken

Express Server Example (server.js)

const express = require('express');
const jwt = require('jsonwebtoken');

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

const SECRET_KEY = 'super-secret-key-change-this-in-production';

// 1. Authentication Route (Issue JWT)
app.post('/api/login', (req, res) => {
  const { username, password } = req.body;

  // Mock credential verification
  if (username === 'admin' && password === 'password123') {
    const userPayload = {
      sub: 'user_123',
      username: username,
      role: 'admin'
    };

    // Sign token with 1-hour expiration
    const token = jwt.sign(userPayload, SECRET_KEY, { expiresIn: '1h' });

    return res.json({
      status: 'success',
      token: token
    });
  }

  return res.status(401).json({ error: 'Invalid username or password' });
});

// 2. Middleware to Verify JWT
function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1]; // Extract token from "Bearer <TOKEN>"

  if (!token) {
    return res.status(401).json({ error: 'Access token required' });
  }

  jwt.verify(token, SECRET_KEY, (err, decodedUser) => {
    if (err) {
      return res.status(403).json({ error: 'Invalid or expired token' });
    }
    req.user = decodedUser;
    next();
  });
}

// 3. Protected Route
app.get('/api/dashboard', authenticateToken, (req, res) => {
  res.json({
    message: `Welcome to the secure dashboard, ${req.user.username}!`,
    user: req.user
  });
});

app.listen(3000, () => {
  console.log('Server listening on http://localhost:3000');
});

5. Experiment & Test JWTs Online

To debug, decode, or generate custom JWT tokens instantly right in your browser, check out our interactive online utility:

👉 JWT Decoder & Generator Tool on mitos.dev

With this tool, you can:

  • Paste any JWT string to inspect its parsed Header and Payload.
  • Verify signatures using your custom secret keys or algorithms (HS256, RS256).
  • Generate new custom JWT tokens for API testing and development experiments.

6. JWT Security Best Practices

To avoid common security pitfalls when using JSON Web Tokens, follow these essential guidelines:

  1. Use Strong Secret Keys:
    When using symmetric algorithms (HS256), ensure your secret key is long, random, and cryptographically secure (at least 256 bits).

  2. Set Short Expiration Times:
    Keep access tokens short-lived (e.g., 15 minutes to 1 hour). Combine short-lived access tokens with Refresh Tokens to maintain secure sessions.

  3. Store Tokens Securely:
    Avoid storing JWTs in localStorage or sessionStorage if your site is vulnerable to Cross-Site Scripting (XSS). Storing JWTs in HttpOnly, SameSite, Secure cookies provides strong protection against token theft via XSS.

  4. Always Verify the Signing Algorithm:
    Ensure your backend explicitly checks and enforces the expected algorithm (e.g., HS256) to prevent alg: "none" exploit vulnerabilities.

  5. Do Not Store Sensitive Information:
    Claims in a standard JWT are Base64URL encoded, not encrypted. Anyone holding the token can decode and view its contents.


Conclusion

JSON Web Tokens (JWT) provide an efficient, stateless, and scalable foundation for web authentication and service-to-service communication. By understanding its three-part structure (Header, Payload, Signature) and adhering to security best practices, you can build reliable authorization systems.

Don't forget to test and experiment with your tokens using the JWT Decoder & Generator Tool!