A JSON Web Token (JWT) is an open standard for securely authenticating users and sending information between a client (like a web or mobile app) and a server.
In traditional web applications, user authentication relied on server-side sessions.
Here’s how it typically worked:
While this approach is simple and effective for small applications, it doesn’t scale well in distributed systems or microservices architectures since it requires maintaining shared session state across servers.
JWT-based authentication takes a different approach. It stores user claims directly inside the token, which remains on the client. The server only needs to verify the token’s signature to authenticate the user, without storing any session data.
This makes them a great choice for building scalable APIs that do not depend on shared memory or server-side sessions.
In this article, we'll cover:
A JWT (JSON Web Token) is a compact, URL-safe token used to securely transmit information between two parties, typically a client and a server.
It is digitally signed, ensuring the data inside hasn’t been tampered with and can be trusted. It’s like a digital ID card that proves who you are.
JWTs are widely used in modern web and mobile applications, especially for authentication and authorization.
Here's why they're so popular:
A JSON Web Token (JWT) is made up of three parts, separated by dots:
header.payload.signature
Each part serves a specific purpose. Let’s break it down.
The header contains metadata about the token.
It usually looks like this:
alg
: The algorithm used to sign the token (e.g., HS256 for HMAC, RS256 for RSA).typ
: The token type, always set to "JWT".This JSON is then Base64Url encoded to form the first part of the token.
The payload contains the claims, information about the user or system. Claims are just pieces of data.
There are three types of claims:
A set of predefined and commonly used claims. Not required, but recommended.
Examples:
iss
: Issuer (who issued the token)sub
: Subject (who the token refers to, often the user ID)aud
: Audience (who the token is intended for)exp
: Expiration time (when the token expires)nbf
: Not before (token becomes valid after this time)iat
: Issued at (when the token was created)jti
: Token ID (unique identifier to prevent reuse)These are custom claims that are shared publicly. Should be unique to avoid conflicts, ideally defined in a public namespace.
These are custom claims created to share information between parties that agree on using them. They are neither registered nor public claims. (e.g., userId, username, roles, permissions).
This JSON is also Base64Url encoded to form the second part of the token.
Note: The payload is encoded, not encrypted. Anyone can decode it, so never put sensitive information like passwords inside.
This is the security part. It's used to verify that the sender of the JWT is who it says it is and to ensure that the message wasn't changed along the way.
Here’s how it’s created:
.
)alg
) and a secret or private keyExample using HMAC SHA-256:
The result is Base64Url encoded and forms the third part of the token.
Here’s a step-by-step look at how authentication typically works using JWTs:
The user initiates a login by sending a POST request with their email and password or social login token:
If the credentials are valid, the authentication server generates a JWT:
exp
).The server sends the generated JWT back to the client, typically in the response body.
The client securely stores the token for use in future requests. Common storage options include:
localStorage
or sessionStorage
(though these are vulnerable to XSS attacks), or preferably an HttpOnly cookie (which cannot be accessed via JavaScript).For every subsequent API call, the client includes the JWT in the Authorization
header using the Bearer schema:
This allows the server to identify and authenticate the user without storing session state.
The server:
Authorization
headerIf the token is valid, the user is authenticated and the server processes the request.
If the token is invalid or expired, the server responds with an appropriate error, such as:
While JWTs are powerful, incorrect implementation can lead to serious vulnerabilities.
Here’s how to use them securely:
Transmit JWTs only over HTTPS to prevent them from being intercepted in transit.
If the secret is leaked, tokens can be forged.
alg: "none"
at all costs. Some older libraries allowed this, disabling signature checks. Make sure your library rejects tokens with alg: none
.This is the core of JWT security. Never trust a token without validating its signature.
These claims are essential for ensuring a token is legitimate and appropriate for your service:
exp
(Expiration Time) – Always set and validate it. Short-lived tokens are generally more secure.nbf
(Not Before) – Optional, but if present, should be checked.aud
(Audience) – Ensure the token was meant for your application.iss
(Issuer) – Confirm the token was issued by a trusted source.JWT payloads are Base64Url-encoded, not encrypted. Anyone can decode and read them. Never include secrets, passwords, or sensitive personal data.
JWTs are stateless, which means once issued, they can’t be revoked easily. You must design with that in mind.
Recommended strategies:
jti
claim to uniquely identify tokens and track their usage (optional and stateful).Best practice for web apps: Use access tokens in memory and refresh tokens in HttpOnly cookies to balance security and usability.