AlgoMaster Logo

JWT

High Priority16 min readUpdated July 4, 2026
AI Mock Interview

Practice this topic in a realistic system design interview

Listen to this chapter
Unlock Audio

Once a system grows past a single server, every service that receives a request needs a way to know who the caller is. Ideally, it can do that without calling the login service on every request. A JSON Web Token (JWT) is one common answer: a small token format used to carry facts about a user, client, or service.

A JWT is a container, not a complete login system. Protocols such as OAuth 2.0 and OpenID Connect decide when tokens are issued, who should receive them, and how refresh works. A signed JWT proves the contents were not changed after signing; it does not automatically mean the API should trust it.

JWTs are useful when many services need to validate the same token without calling back to the issuer each time. The trade-off is revocation. Once a signed token is issued, it may be accepted until it expires unless you add a server-side check to reject it earlier.

This chapter explains how a JWT is signed, validated, stored, and revoked.

1. What Is a JWT?

A JWT is a compact token that carries claims and is usually protected by a digital signature.

Loading simulation...

A claim is a fact written inside the token. A token might say that user 123 is the caller, that https://auth.example.com issued the token, that only orders-api should accept it, that it expires at a specific time, and that it allows orders:read.

Most JWTs used as access tokens are signed. A signed JWT is called a JWS: JSON Web Signature. Signing protects integrity. In plain English, it lets the API detect whether someone changed the token after it was issued.

Signing does not hide the data. The payload is Base64Url-encoded, not encrypted. Anyone who gets the token can decode and read its claims.

Encrypted JWTs also exist. They are called JWE: JSON Web Encryption. They are less common for API access tokens because they add more key-management work. Most systems use signed JWTs and avoid putting sensitive data inside them.

When JWTs Are Useful

JWTs are useful when several services need to validate the same access token, especially when the login or authorization server is separate from the APIs.

They help when APIs need to validate tokens locally for speed or availability, when tokens must carry issuer, audience, expiry, scopes, or tenant context, and when external clients call APIs using tokens issued through OAuth 2.0 or OIDC.

They are not automatically better than sessions. A server-side session is often the right answer for a first-party browser app because the server can revoke it immediately and keep the client credential small.

The practical question is: who needs to validate the token, and how quickly must you be able to revoke it?

2. Anatomy of a JWT

A JWT has three parts separated by dots:

Expanding each of those three parts shows what they hold:

Each part has a separate role.

The header describes how the token was signed.

Common header fields:

FieldMeaning
algSigning algorithm, such as RS256, ES256, or HS256
typToken type, commonly JWT
kidKey ID used to find the right verification key

Do not trust the header blindly. The API must allow only the algorithms and keys it expects. The header can help pick a key, but it must not decide the security rules.

Payload

The payload contains claims, which are facts the issuer is stating.

Registered claims are common claim names with agreed meanings:

ClaimMeaning
issIssuer: who issued the token
subSubject: who or what the token represents
audAudience: which service should accept the token
expExpiration time
nbfNot valid before
iatIssued at
jtiToken ID

Applications can also define their own claims, such as tenant_id, scope, or roles.

Example payload:

The payload is visible to anyone holding the token. Do not put passwords, API keys, payment data, recovery codes, or sensitive personal data in JWT claims.

Also avoid putting fast-changing permissions in long-lived JWTs. If a user's role changes from admin to member, an old token with admin may remain valid until it expires unless you check current server-side state.

Signature

The signature protects the header and payload from being changed silently.

Conceptually, the signer computes:

The API recomputes the signature input and checks it with a trusted verification key.

There are two common key models:

ModelExample algorithmsHow it worksGood fit
SymmetricHS256Same secret signs and verifiesOne issuer and a small trusted set of verifiers
AsymmetricRS256, ES256, EdDSAPrivate key signs, public key verifiesMany services validating tokens from one issuer

Asymmetric signing is common in OAuth and OIDC systems because APIs can verify tokens using public keys without receiving the issuer's private signing key.

3. How JWT Authentication Works

JWTs often appear in an access-token flow. The idea is simple: the auth server issues the token, and the API checks it before serving the request.

1. Authenticate (credentials or OAuth flow)2. Validate user, sign JWT with private keyAccess token (JWT)3. GET /resourceAuthorization: Bearer JWT4. Verify signature, iss, aud, exp,algorithm, required scopesProtected resourceClientAuth ServerResource Server
6 / 6
algomaster.io

Step 1: The Client Authenticates

The user signs in through an authorization server or identity provider.

In modern OAuth/OIDC flows, the application usually does not handle the user's password directly. It redirects the user to the identity provider and receives tokens after sign-in succeeds.

Step 2: The Issuer Creates a Token

After sign-in and policy checks, the issuer creates a short-lived access token.

Example in application code:

In a production identity system, an authorization server usually creates tokens, not each application service.

Step 3: The Client Sends the Token

The client includes the access token on API requests.

The token is a bearer credential. Whoever has it can use it until it expires or the server rejects it.

Step 4: The API Validates the Token

Before trusting the token, the API should validate the signature and confirm the signing algorithm is one it allows.

It should check the expected issuer (iss) and audience (aud), the expiration (exp), and the not-before time (nbf) when present. It should also enforce the required scopes or permissions, the tenant or account boundary in a multi-tenant system, and the key ID (kid) against a trusted key set when one is present.

If validation fails, return 401 Unauthorized. If the token is valid but does not grant enough permission for the operation, return 403 Forbidden.

4. Key Distribution and Rotation

JWT systems fail when key management is treated as an afterthought.

Symmetric Keys

With HS256, the same secret signs and verifies tokens.

That means every service that can validate a token can also create one if it has the secret. This can be acceptable inside a small, tightly controlled system. It is a poor fit when many services, teams, or third parties need to verify tokens.

If you use symmetric keys, generate strong random secrets, store them in a secrets manager, rotate them on a schedule, avoid sharing them broadly, and use separate secrets for separate environments and issuers.

Asymmetric Keys and JWKS

With RS256, ES256, or EdDSA, the issuer signs with a private key. APIs verify with a public key.

Large systems usually publish public verification keys through a JWKS endpoint. Think of JWKS as a URL where APIs can fetch the issuer's public keys.

The token header includes a kid. The API uses that key ID to select the matching public key from the trusted JWKS.

Good key rotation usually has overlap:

  1. Publish the new public key.
  2. Start signing new tokens with the new private key.
  3. Keep the old public key available until all old tokens expire.
  4. Remove the old key after the overlap window.

Do not fetch arbitrary JWKS URLs from token contents. The issuer and JWKS location must come from trusted server configuration.

5. Access Tokens, ID Tokens, and Refresh Tokens

JWTs can be used for different token types. Mixing them up creates security bugs.

TokenWho consumes itPurpose
Access tokenResource server or APIGrants API access
ID tokenClient applicationTells the client who authenticated
Refresh tokenAuthorization serverGets new access tokens

An ID token from OpenID Connect is not an API access token. It tells the client who signed in. APIs should not accept ID tokens as proof that the caller can access protected endpoints.

A refresh token should not be sent to normal APIs. It should be sent only to the authorization server's token endpoint. Refresh tokens usually need rotation, revocation, reuse detection, and stronger storage controls than access tokens.

An access token should be short-lived, limited to the API it calls, and scoped to the permissions it needs.

6. Token Storage

Token storage depends on the client type.

Browser Applications

Browser token storage is a trade-off between XSS, CSRF, usability, and architecture. XSS means attacker-controlled JavaScript runs in your page. CSRF means a browser sends a cookie-backed request the user did not intend.

Common options:

StorageMain riskNotes
localStorage / sessionStorageJavaScript can read tokens after XSSAvoid for high-value credentials
In-memory JavaScript variableLost on refresh, still usable by injected scriptsBetter than storage that JavaScript can read later
HttpOnly cookieSent automatically, so CSRF must be handledProtects token from direct JavaScript reads
Backend-for-frontend sessionRequires server-side stateStrong choice for first-party web apps

For many first-party web applications, an HttpOnly, Secure, SameSite cookie carrying an opaque session ID is simpler and safer than putting JWTs in browser storage. Opaque means the client cannot read meaning from it; the server looks it up.

If a browser app uses tokens, keep access tokens short-lived, prefer refresh tokens in HttpOnly, Secure, SameSite cookies, use CSRF protection for cookie-authenticated state-changing requests, avoid long-lived JavaScript-readable storage for refresh tokens, and use a strong Content Security Policy to reduce XSS risk.

Mobile and Native Apps

Use platform secure storage: iOS Keychain, Android Keystore-backed storage, or OS credential vaults for desktop apps. Do not store long-lived tokens in plain files, logs, crash reports, analytics events, or screenshots.

Backend Services

Backend services should avoid hardcoded long-lived JWTs. Prefer cloud or platform workload identity, short-lived service tokens, mTLS or signed service-to-service credentials, and secrets stored in a managed secrets system.

7. Revocation and Expiration

The hardest operational problem with JWTs is revocation, which means making a token stop working before its expiration time.

A self-contained JWT can be validated without contacting the issuer. That is useful for speed and availability, but it means the issuer cannot automatically pull the token back after it leaves.

Common strategies:

StrategyHow it worksTrade-off
Short access-token lifetimeToken expires quickly, such as 5-15 minutesStolen token works until expiry
Refresh token rotationEach refresh returns a new refresh tokenMore server-side tracking
Revocation listStore revoked jti or session IDsAdds a server-side lookup
Token introspectionAPI asks issuer whether token is activeAdds latency and depends on issuer
Session version claimToken includes a user/session version checked by APIRequires lookup for sensitive operations

Short-lived access tokens plus refresh-token rotation are common. For high-risk actions, many systems still check server-side state even if the access token is a valid JWT. That extra check is typical when a user changes payout details, exports sensitive data, disables MFA, creates API keys, or performs administrative actions.

Stateless validation is a performance optimization. It should not stop the system from making live authorization checks when the risk requires them.

8. Security Checklist

JWTs are easy to parse and easy to validate incorrectly. A safe implementation is strict.

Validate More Than the Signature

Always validate the signature using a trusted key and an algorithm fixed in server configuration. Check iss, aud, and exp, along with nbf when it is present. Then enforce the required scopes, roles, or permissions and the tenant or organization boundary.

Do not accept a token only because the signature is valid. A valid token for billing-api should not be accepted by orders-api.

Allowlist Algorithms

Never let the token choose the algorithm policy.

Configure acceptable algorithms explicitly, such as:

Reject alg: "none" and reject unexpected algorithm families. Algorithm confusion bugs happen when an API accepts a token signed with a different algorithm than the service intended.

The classic version of this attack is RS256-to-HS256 confusion. The issuer signs tokens with an RSA private key and publishes the matching public key. The API reads alg from the token header. An attacker re-signs a forged token with alg: "HS256", using the issuer's public key as the HMAC secret.

If the API blindly trusts the header and uses the same key, the forged token may be accepted. The fix is to configure acceptable algorithms on the server and choose the verification key based on that configuration, not on the token header alone.

Allow Clock Skew for exp and nbf

Issuer and API clocks can drift. A token issued near its exp time can look expired to an API whose clock is a few seconds ahead, even when nothing is wrong.

Production APIs usually allow a small leeway, often 30 to 60 seconds, for exp and nbf. Larger leeway weakens the freshness of short-lived access tokens, so do not stretch it into minutes.

Defend Against Replay With jti

A bearer token can be reused by anyone who captures it. Signature checks alone do not stop replay.

For high-value or single-use tokens, include a unique jti, cache seen jti values until the token's exp, and reject duplicates. This makes the token usable only once at that API. For ordinary short-lived access tokens, teams usually rely on short lifetimes and audience binding; full jti tracking is usually reserved for sensitive operations.

Keep Claims Small and Stable

JWTs are sent on every request. Large tokens increase request size, logging risk, gateway header limits, and latency.

Good claims identify the subject, name the issuer and audience, expire the token, carry stable scopes or broad permissions, and add tenant or account context when needed. Poor claims try to stuff in a full user profile, secrets, credentials, large permission graphs, data that changes every few seconds, or sensitive business and personal data.

Be Careful with Roles

Roles in a JWT are snapshots. They may be stale.

For broad access checks, a scope or role claim can be fine. For sensitive decisions, check current server-side authorization state.

For example, a JWT may say the user has admin. Before deleting a production project, the API may still check the current membership table, account status, MFA freshness, or risk policy.

Do Not Log Tokens

JWTs leak through the obvious places: authorization headers, reverse proxy logs, application error logs, browser console output, analytics events, and crash reports. Treat them as secrets, redact them before logging, and remember that a signed JWT is still a bearer credential.

Separate Environments

Production, staging, and development should use different issuers, audiences, and signing keys.

Do not let staging tokens work against production APIs. Do not use production identity provider keys in local development.

9. Common Mistakes

Avoid these mistakes:

  1. Treating JWTs as encrypted: Signed JWTs are readable by anyone holding them.
  2. Skipping aud validation: Tokens meant for one API can be replayed to another.
  3. Trusting the alg header: The server must enforce allowed algorithms.
  4. Using long-lived access tokens: Stolen bearer tokens remain usable for too long.
  5. Putting sensitive data in claims: Tokens spread through clients, logs, traces, and proxies.
  6. Using ID tokens as API access tokens: ID tokens are for clients, not resource servers.
  7. Ignoring key rotation: Old keys, missing kid, and no overlap window cause outages or security gaps.
  8. Assuming JWTs eliminate server-side state: Revocation, refresh tokens, risk checks, and active sessions often need state.

Summary

JWT is a token format, not a complete authentication system. It is useful when APIs need to validate signed claims locally, especially in OAuth and OIDC-based systems. Its main benefits are small tokens, local verification, and clear token fields such as issuer, audience, expiry, and scope.

The main risks are stale authorization, token theft, poor storage, weak validation, bad key management, and confusion between token types.

Use JWTs when their trade-offs fit the system. Keep access tokens short-lived, validate issuer and audience, allowlist algorithms, rotate keys, avoid sensitive claims, and add revocation or live authorization checks where the business risk requires it.

Quiz

JWT Quiz

10 quizzes