
Master the fundamentals of JSON Web Tokens, their role in OAuth 2.0 and OIDC, and the security considerations necessary for modern authentication. Learn when to implement JWTs versus traditional server-side sessions.
Understanding JSON Web Tokens (JWT)
A JSON Web Token (JWT) is a compact, URLâsafe string that carries a set of claims between two parties. Its core structure consists of two Base64URLâencoded JSON objects separated by a period (.):
(Header).(Payload)
The JOSE header describes how the token is processed. At minimum it contains:
"alg"â the algorithm used for signing (e.g.,HS256) or"none"when no integrity protection is applied."typ"â typically set to"JWT"to identify the token type.
Example header (JSON):
{
"typ": "JWT",
"alg": "HS256"
}
The payload (or claims) holds the data the token conveys, such as user identifiers, roles, or expiration timestamps. Claims are plain JSON keyâvalue pairs:
{
"id": "1234567890",
"name": "John Doe",
"age": 36
}
Both header and payload are UTFâ8 encoded, then Base64URLâencoded to ensure URL safety. An unsecured tokenâwhere "alg":"none"âlooks like:
eyJhbGciOiJub25lIn0.eyJpZCI6IjEyMzQ1Njc4OTAiLCJuYW1lIjoiSm9obiBEb2UiLCJhZ2UiOjM2fQ
Because there is no cryptographic binding, any party can modify the payload and reâencode it, breaking integrity.
To protect against tampering, the token is transformed into a JSON Web Signature (JWS) by appending a third component: the signature. The signature is computed by hashing the Base64URLâencoded header and payload with the algorithm declared in "alg" and a secret (HMAC) or private key (RSA/ECDSA). The resulting binary signature is then Base64URLâencoded and concatenated:
(Header).(Payload).(Signature)
Example signed JWT:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEyMzQ1Njc4OTAiLCJuYW1lIjoiSm9obiBEb2UiLCJhZ2UiOjM2fQ.4SkNQ2QZ8z5Lh7W0n2FK8KnXxXq_9yPmyMslK9YpN0A
In this form, any alteration to the header or payload invalidates the signature, allowing the receiver to verify integrity before trusting the claims. This transition from plain Base64URL tokens to signed JWS tokens is the fundamental security step required for authentication and authorization workflows.
JWTs in the OAuth 2.0 and OIDC Landscape
JSON Web Token (JWT) is a JWT format defined by the JOSE specifications. It consists of a Base64URLâencoded header, payload (claims), and an optional signature. The format itself does not prescribe how a token is obtained or where it is used; that is the responsibility of higherâlevel protocols such as OAuthâŻ2.0 and OpenID Connect (OIDC).
OAuthâŻ2.0 is an authorization framework that describes how a resource owner can grant a client limited access to a protected resource. The framework does not mandate a token representation, but in practice most implementations issue access tokens as JWTs because they are selfâcontained and can be validated without a network call to the authorization server.
OIDC builds an identity layer on top of OAuthâŻ2.0. It introduces the ID token, which is always a JWT and carries authenticationârelated claims (e.g., sub, email, name). The ID token is intended for the client application to establish the userâs identity; it is not meant to be presented to APIs for authorization.
A typical OIDC flow therefore returns three distinct tokens:
- ID token (JWT) â asserts who the user is; consumed by the client to create a session.
- Access token (often JWT) â presented to resource servers (APIs) to prove the callerâs permission; APIs must validate its signature and scope.
- Refresh token (opaque or JWT) â stored securely by the client and used only with the token endpoint to obtain new access (and optionally ID) tokens; never sent to the API.
Example: after a user clicks âSign in with Google,â the authorization server returns an ID token containing {"sub":"1234567890","email":"user@example.com"}, an access token like eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9âŠ, and a refresh token such as 1//0gX... . The client stores the ID token in memory (or a shortâlived cookie) for UI decisions, uses the access token in the Authorization: Bearer header when calling https://api.example.com/userinfo, and exchanges the refresh token when the access token expires.
Key takeâaways for engineers:
- Never use an ID token as an access token; APIs must validate the access tokenâs audience and scopes.
- Refresh tokens should be kept out of browserâaccessible storage (e.g., use httpOnly cookies) to reduce exposure to XSS.
- When the access token is a JWT, verify its signature, expiration (
exp), and audience (aud) on every request.
Why Use JWTs for Authentication?
Signed JSON Web Tokens (JWS) are selfâcontained data structures that combine a header, a payload of claims, and a cryptographic signature. The signatureâtypically generated with HMACâSHAâ256 or an RSA/ECDSA algorithmâensures that only a holder of the secret key (or private key) can create a token that will successfully verify on the server.
Because the payload is trusted after verification, the token can replace traditional session identifiers and, crucially, eliminates the need to store user passwords or session IDs in clientâside storage such as localStorage or cookies. An example of a minimal authentication flow using a signed token is:
// Server side (Node.js)
const jwt = require('jsonwebtoken');
const token = jwt.sign({ sub: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: '15m' });
res.json({ accessToken: token });
When the client includes this token in the Authorization: Bearer header, the API can validate the signature and read the claims without any additional lookup.
Key advantages for enterprise applications
- Eliminates local password storage. The token carries a user identifier (e.g.,
sub) and any required attributes. Since the signature guarantees integrity, the client never needs to retain the password after the initial login, reducing the attack surface highlighted by OWASPâs authentication cheat sheet. - Reduces redundant database queries. Claims such as role, tenant ID, or permission scopes are embedded in the JWT. After signature verification, downstream services can make authorization decisions based on these claims alone, avoiding a roundâtrip to the user store for each request. This aligns with NIST SPâŻ800â63 recommendations for stateless tokenâbased authentication.
- Supports horizontal scaling. Because verification requires only the shared secret or public key, any instance of a microservice can authenticate requests without session affinity, simplifying loadâbalancer configuration and complying with ISOâŻ27001 requirements for consistent access control.
In practice, an API endpoint might look like:
// Express middleware
function authenticate(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
req.user = payload; // payload contains id, role, etc.
next();
} catch (err) {
res.sendStatus(401);
}
}
By trusting the verified payload, the service can authorize the request without querying the user database, thereby improving latency and reducing load on credential stores while maintaining compliance with security standards.
Security Considerations: Storage and Attacks
When a JSON Web Token (JWT) is used for authentication, the location where the token is stored determines the applicationâs exposure to two of the most common webâapplication attacks: crossâsite scripting (XSS) and crossâsite request forgery (CSRF). Understanding the mechanics of each attack helps engineers choose a storage mechanism that aligns with the organizationâs security controls (e.g., SOCâŻ2, ISOâŻ27001, NISTâŻ800â63).
Storage choices and their attack surface
- LocalStorage / sessionStorage â Tokens are accessible to any JavaScript running in the origin. If an attacker injects malicious script (XSS), they can read the token and replay it against the API. These storages do not send the token automatically with every request, so they are not vulnerable to CSRF, but they are highly susceptible to XSS.
- HttpOnly, Secure cookies â The browser includes the cookie on every request to the same origin, which eliminates the need for clientâside code to read the token. Because JavaScript cannot access HttpOnly cookies, XSS cannot steal them directly. However, the automatic inclusion makes the application vulnerable to CSRF unless additional mitigations (sameâsite attribute, antiâCSRF tokens) are applied.
- Inâmemory variables â Storing the token only in a JavaScript variable (e.g., after a successful login) limits persistence across page reloads. This reduces the window for XSS theft but does not protect against CSRF if the token is later placed in a cookie.
Mitigating tokenârelated attacks
Regardless of storage, two token lifecycle controls are essential:
- Accessâtoken expiration â Shortâlived access tokens (minutes to hours) limit the usefulness of a stolen token. The server validates the
expclaim on each request, rejecting expired tokens. - Refreshâtoken rotation â When a client presents a valid refresh token, the authorization server issues a new access token **and** a new refresh token, revoking the previous one. If an attacker captures a refresh token, the next legitimate rotation invalidates it, reducing replay risk.
Practical example
// Example of refreshâtoken rotation (Node.js/Express)
app.post('/token/refresh', async (req, res) => {
const oldToken = req.body.refreshToken;
const payload = await verifyRefreshToken(oldToken); // validates, checks revocation
const newAccess = signAccessToken(payload.sub);
const newRefresh = signRefreshToken(payload.sub);
await revokeToken(oldToken); // ensures rotation
res.json({ accessToken: newAccess, refreshToken: newRefresh });
});
By pairing HttpOnly, SameSite cookies with shortâlived access tokens and implementing refreshâtoken rotation, engineers can mitigate both XSS and CSRF vectors while maintaining compliance with widely accepted security frameworks.
JWTs vs. Server-Side Sessions
JSON Web Tokens (JWTs) are signed, selfâcontained strings that convey claims about a user without requiring a database lookup on each request. While this stateless model simplifies scaling, the LogRocket guide lists several practical limitations that affect security and operational control.
Key limitations of JWTs
- Revocation difficulty: Because a JWT is valid until its expiration, a server cannot invalidate a single token without maintaining a blacklist, which reâintroduces state.
- Token size: The base64urlâencoded header, payload, and signature increase request size, which can impact bandwidth and header limits.
- Exposure to XSS: Storing a JWT in
localStorageorsessionStoragemakes it readable by malicious scripts, increasing the risk of token theft. - CSRF considerations: When a JWT is stored in a cookie, it is automatically sent with every request, so an attacker can exploit CSRF unless sameâsite or doubleâsubmit cookie patterns are used.
- No builtâin encryption: JWTs are typically signed but not encrypted; any sensitive data placed in claims is visible to anyone who can read the token.
- Compliance impact: Standards such as SOCâŻ2, ISOâŻ27001, NIST SPâŻ800â63, and OWASP ASVS require mechanisms for session termination and audit logging, which are harder to guarantee with purely stateless tokens.
When serverâside sessions are a better fit
- Immediate revocation is required (e.g., after password change, account lockout, or suspicious activity).
- Sensitive user data must never travel to the client; storing a session identifier on the server keeps the data protected.
- The application must comply with strict audit and accessâcontrol requirements that mandate explicit session termination.
- Lowâlatency environments where the overhead of token verification and large headers is undesirable.
- Legacy APIs that expect a traditional session cookie rather than an Authorization header.
Practical example: In an Express.js service, using express-session with an HttpOnly cookie stores only a random session ID on the client. The server retains the full user profile, can destroy the session instantly, and benefits from builtâin CSRF protection via sameâsite cookies. By contrast, a JWT stored in localStorage would require additional clientâside safeguards and a serverâside blacklist to achieve comparable revocation capability.
Looking for Custom Software or AI Solutions?
Appworks Technologies designs, builds, and scales production enterprise platforms, microservices, and AI agent workflows tailored to your business goals.
Editorial Policy & Research Methodology
Our findings are based on rigorous internal research, verified industry benchmarks, and direct technical implementation experience from our enterprise client projects. All statistics and technical claims are reviewed by senior engineers before publication to ensure accuracy, transparency, and helpfulness for our readers.
