Skip to content

JSON Web Tokens (JWT)

Technical Overview

A JSON Web Token (JWT) is a compact, URL-safe token format used to securely transmit claims between parties. It is defined in RFC 7519 and is most commonly used for authentication and authorization in web APIs.

JWT is language-agnostic, it’s just JSON-based.

Structure of a JWT

A JWT consists of three Base64URL-encoded parts separated by dots:

header.payload.signature

Example:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlN0dWFydCIsImlhdCI6MTUxNjIzOTAyMn0
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

1. Header

Contains metadata about the token:

{
  "alg": "HS256",
  "typ": "JWT"
}
  • alg → signing algorithm (e.g. HS256, RS256)
  • typ → token type (JWT)

2. Payload

Contains claims (statements about the subject).

Example:

{
  "sub": "1234567890",
  "name": "Stuart",
  "role": "EngineeringManager",
  "iat": 1516239022,
  "exp": 1516242622
}

Claims are either:

Type Description
Registered claims Standardised (e.g., iss, sub, aud, exp, iat)
Public claims Custom, but collision-resistant
Private claims Application-specific

Important standard claims:

  • iss — Issuer
  • sub — Subject (user ID)
  • aud — Audience (intended recipient)
  • exp — Expiry time
  • nbf — Not before
  • iat — Issued at
  • jti — Token ID

3. Signature

Created by signing:

base64UrlEncode(header) + "." + base64UrlEncode(payload)

Using the specified algorithm and a secret or private key.

Example for HS256:

HMACSHA256(
  secret,
  encodedHeader + "." + encodedPayload
)

What JWT Actually Solves

JWT enables stateless authentication.

Instead of:

  • Storing session state on the server (e.g., ASP.NET session, server-side session store)
  • Looking up session data on every request

You:

  • Issue a signed token after login
  • Client stores it (usually in memory or secure storage)
  • Client sends it on each request (typically in Authorization: Bearer <token>)
  • Server validates signature and claims
  • No server-side session required

This is especially useful for:

  • Microservices
  • Distributed systems
  • APIs behind gateways
  • SPA + API architectures
  • Mobile apps

Symmetric vs Asymmetric Signing

HS256 (Symmetric)

  • Shared secret used to sign and validate
  • Simpler
  • All services validating must know the same secret
  • Harder to rotate securely at scale

RS256 / ES256 (Asymmetric)

  • Private key signs
  • Public key validates
  • Much better for distributed architectures

Typical modern pattern:

  • Identity Provider (e.g., Azure AD, Auth0, Okta) signs with private key
  • APIs validate using public key via JWKS endpoint

For enterprise microservices, asymmetric is strongly preferred.

JWT in Authentication Flow (Typical OIDC Scenario)

  1. User logs in via Identity Provider
  2. IdP returns:

  3. ID token (JWT)

  4. Access token (JWT)

  5. Client sends access token to API

  6. API:

  7. Validates signature

  8. Validates iss, aud, exp
  9. Extracts claims
  10. Authorises request

Important distinction:

Token Type Purpose
ID Token Identifies the user (client-side use)
Access Token Authorises API calls
Refresh Token Gets new access tokens

Security Considerations (Critical)

JWT is widely misunderstood. Key issues:

1. Payload Is Not Encrypted

JWT is signed, not encrypted (unless using JWE).

Anyone can decode the payload. Never put secrets in a JWT.

2. Revocation Is Hard

Because JWTs are stateless:

  • You can’t easily revoke them.
  • Short expiry times are best practice (e.g., 5–15 minutes).
  • Use refresh tokens for renewal.

3. Token Storage Risks

For SPAs:

  • Avoid localStorage (XSS risk).
  • Prefer in-memory storage + silent refresh.
  • For server-rendered apps, HTTP-only secure cookies are safer.

4. Always Validate

  • Signature
  • Expiry (exp)
  • Issuer (iss)
  • Audience (aud)
  • Not before (nbf)

Many security incidents come from partial validation.

JWT in .NET

In ASP.NET Core:

builder.Services
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = "https://login.microsoftonline.com/{tenant}";
        options.Audience = "api://your-api-id";
    });

This automatically:

  • Fetches signing keys from JWKS
  • Validates signature
  • Validates issuer & audience
  • Populates ClaimsPrincipal

Then:

[Authorize]
[HttpGet]
public IActionResult SecureEndpoint()
{
    return Ok(User.Claims);
}

When NOT to Use JWT

JWT is overused. Avoid it when:

  • You only have a monolith
  • You don’t need stateless auth
  • You need immediate revocation
  • You want very small cookies (JWTs are verbose)

In those cases, traditional server-side sessions may be simpler.

Architecture Perspective

From a systems design standpoint, JWT:

  • Enables horizontal scalability
  • Reduces session store dependency
  • Pushes identity verification to token validation
  • Works well behind API gateways
  • Aligns with zero-trust architecture principles

In distributed retail or fraud-prevention systems (like event-driven microservices), JWT commonly sits at the edge (API gateway) rather than deep inside service-to-service communication.

JWT vs OAuth vs OIDC

Concept What It Is
JWT Token format
OAuth2 Authorisation framework
OIDC Identity layer on top of OAuth2

JWT is just the container — OAuth/OIDC define how tokens are issued and used.