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— Issuersub— Subject (user ID)aud— Audience (intended recipient)exp— Expiry timenbf— Not beforeiat— Issued atjti— 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)
- User logs in via Identity Provider
-
IdP returns:
-
ID token (JWT)
-
Access token (JWT)
-
Client sends access token to API
-
API:
-
Validates signature
- Validates
iss,aud,exp - Extracts claims
- 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.