Stage 9. JWT Basics
What is JSON Web Token?
JSON Web Token (JWT) is an open standard from RFC 7519. It defines a compact, self-contained way to transfer claims as a JSON object between parties. A JWT can be trusted only when it is correctly signed and validated. In practice, signatures are created either with a shared secret (HMAC) or with asymmetric keys (for example RSA or ECDSA).
JWT can also be encrypted, but most API integrations rely on signed tokens. A signed token gives integrity: receiver can verify that claims were not changed in transit. With asymmetric signing, receiver can also verify that token was created by the party that owns the private key.
JWT is commonly used for authorization. After login, client sends token on subsequent requests, and API decides access based on verified claims. JWT is also useful for secure information exchange between trusted systems, since signature validation proves sender authenticity and payload integrity.
In compact form, JWT has three dot-separated parts:
header.payload.signature
The header usually contains token type and algorithm:
{
"typ": "JWT",
"alg": "HS256"
}
The payload contains claims. Claims are statements about subject and context. They are usually grouped into:
- Registered claims (
iss,sub,aud,exp, and others). - Public claims (namespaced or registry-based to avoid collisions).
- Private claims (custom fields agreed between systems).
Example payload:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
JWT is often treated as “secure by default,” but that is a risky assumption. JWT is only a message format. Real security depends on how tokens are issued, transported, validated, and revoked. If those processes are weak, even signed tokens can fail to protect your APIs.
A production-grade rule is straightforward: every service that receives a JWT should validate it fully. Checking only exp is not enough. At minimum, validate signature, iss, aud, expiration, required domain claims, and token purpose/type. It is also critical not to mix token types: ID tokens are for clients, access tokens are for APIs. Treating them as interchangeable creates both security and reliability issues.
Authorization: Bearer <access-token>
Payload design is another frequent risk. JWTs are easy to decode, so avoid putting sensitive personal data or internal technical details in claims. For front-channel usage, keep claims minimal and fetch user profile data from dedicated APIs when needed. In stricter architectures, teams use patterns where external clients handle opaque tokens while internal APIs receive JWTs only behind an API gateway.
Header validation is essential, especially alg. Servers should enforce an explicit allow-list of accepted algorithms. Deny-lists are weaker and historically easier to bypass. Unsigned JWTs (alg=none) should not be accepted in normal API flows. For signatures, asymmetric crypto is preferred. Where possible, modern options such as EdDSA or ES256 are often favored over legacy defaults.
Most JWT access tokens are bearer tokens, meaning possession is enough for use. If that is too weak for your threat model, use sender-constrained tokens (for example mTLS- or PoP/DPoP-based approaches) so a stolen token cannot be replayed from another client.
JWT revocation is inherently difficult after issuance, so access tokens should be short-lived (typically minutes, sometimes hours). Validate time claims beyond expiration: nbf and iat are also useful controls. Apply small clock-skew tolerance (usually seconds, not minutes). Long-lived tokens dramatically increase breach impact.
Finally, JWT security depends on key operations maturity: centralized key management, rotation schedule, overlap window, emergency rollover, and reason-level observability (bad_signature, wrong_issuer, wrong_audience, token_expired). JWT scales safely only when treated as a managed security subsystem, not just a header string.
Practical scenario
An organization has multiple APIs validating tokens differently: one checks only signature and exp, another validates aud, and a third accidentally accepts ID tokens as access tokens. Result: inconsistent 401 behavior and difficult incident triage. After unifying validation policy (token type, iss, aud, claims, algorithm, time checks) and key rotation process, behavior becomes stable and predictable.