JSON Web Token, commonly known as JWT, has become one of the most widely used standards for securely transmitting information between applications, APIs, services, and users. JWT is particularly popular in modern web applications because it provides a compact way to represent claims between two parties and can be easily transmitted through HTTP headers, URLs, cookies, and other mechanisms.
JWT is commonly associated with authentication and authorization, but its capabilities extend beyond simply logging users into applications. It can be used to securely communicate identity information, permissions, session-related data, and other claims between different systems.
This comprehensive guide explains what JWT is, where it came from, how it works, its structure, encoding and signing process, encryption, verification, authentication flow, practical examples, security considerations, advantages, disadvantages, and common mistakes.
What Is JWT?
JWT stands for JSON Web Token.
It is a compact, URL-safe format used to represent claims that can be transferred between two parties. These claims are represented as a JSON object and are typically digitally signed so that the recipient can verify that the information has not been modified.
A JWT usually looks like this:
xxxxx.yyyyy.zzzzz
It contains three components:
Header.Payload.Signature
For example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIxMjM0NTYiLCJuYW1lIjoiTm9tYW4iLCJpYXQiOjE3MjAwMDAwMDB9. SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
These three sections are:
- Header
- Payload
- Signature
One important point is often misunderstood:
A normal JWT is encoded and signed, not encrypted.
Anyone who obtains a normal JWT can decode its header and payload. The signature prevents unauthorized modification, but it does not hide the contents.
Origin and History of JWT
JWT is not an isolated technology. It belongs to a broader family of Internet standards developed around JSON-based security and identity information.
JWT was standardized by the Internet Engineering Task Force (IETF) through the JSON Web Token (JWT) specification, RFC 7519. RFC 7519 was published in May 2015.
JWT was designed as part of a group of specifications collectively known as JOSE — JSON Object Signing and
Encryption.
The JOSE family includes several important standards:
- JWS — JSON Web Signature
- JWE — JSON Web Encryption
- JWK — JSON Web Key
- JWA — JSON Web Algorithms
- JWT — JSON Web Token
JWT itself defines how claims are represented, while other JOSE specifications define how those claims can be digitally signed or encrypted.
JWT became particularly popular as web development moved toward:
- REST APIs
- Single-page applications
- Mobile applications
- Microservices
- Cloud applications
- Distributed systems
- OAuth 2.0 and OpenID Connect
Before JWT became common, applications frequently relied on traditional server-side sessions. Those sessions remain useful, but JWT introduced a convenient token-based approach that works particularly well across distributed systems.
Why Was JWT Created?
Traditional authentication often works like this:
- User logs in.
- Server verifies username and password.
- Server creates a session.
- Session information is stored on the server.
- Browser receives a session identifier.
- Browser sends that identifier with future requests.
- Server looks up the session.
This works well, but maintaining sessions across many servers can introduce additional infrastructure requirements.
For example, suppose an application has:
Server A Server B Server C
If the user logs in through Server A but the next request goes to Server C, Server C needs access to the session information.
This can be solved using shared session storage such as Redis or a database, but it introduces additional infrastructure.
JWT provides another approach.
Instead of storing all authentication information in a server-side session, the server can issue a signed token containing relevant claims.
The client then sends the token with subsequent requests.
Understanding the Three Parts of JWT
A JWT consists of three Base64URL-encoded components separated by periods.
Header.Payload.Signature
Let's examine each part.
1. Header
The header contains information about the token itself.
A typical header looks like:
{
"alg": "HS256",
"typ": "JWT"
}
The two common properties are:
alg
alg specifies the cryptographic algorithm.
For example:
HS256
means HMAC using SHA-256.
Other algorithms include:
RS256 RS512 ES256 PS256
typ
typ identifies the token type.
For JWT it is commonly:
JWT
The header is then Base64URL encoded.
2. Payload
The payload contains the claims.
For example:
{
"sub": "123456",
"name": "Muhammad Noman",
"role": "admin",
"iat": 1720000000,
"exp": 1720003600
}
Claims can describe information such as:
- User ID
- Username
- Role
- Permissions
- Token issuer
- Token audience
- Token creation time
- Expiration time
JWT defines several registered claim names.
Important claims include:
iss — Issuer
Identifies who issued the token.
"iss": "https://example.com"
sub — Subject
Identifies the subject of the token.
For authentication, this is commonly a user identifier.
"sub": "123456"
aud — Audience
Identifies the intended recipient of the token.
"aud": "api.example.com"
exp — Expiration Time
Specifies when the token expires.
"exp": 1720003600
iat — Issued At
Indicates when the token was issued.
"iat": 1719990000
nbf — Not Before
Specifies the time before which the token must not be accepted.
"nbf": 1720000000
jti — JWT ID
Provides a unique identifier for the token.
"jti": "8f14e45f-ea..."
Applications can also define custom claims.
For example:
{
"sub": "123456",
"role": "manager",
"department": "finance"
}
However, sensitive information should not normally be placed in a standard signed JWT because its payload is readable.
3. Signature
The signature is what allows the recipient to detect unauthorized modifications.
For an HMAC-based JWT, conceptually:
HMACSHA256(
Base64URL(Header) + "." + Base64URL(Payload),
SecretKey
)
The resulting signature is encoded and appended to the token.
Therefore:
JWT = Base64URL(Header) + "." + Base64URL(Payload) + "." + Base64URL(Signature)
If somebody changes the payload, the signature no longer matches.
Encoding vs Encryption
This is one of the most important JWT concepts.
JWT commonly uses Base64URL encoding.
Encoding is not encryption.
For example:
{
"name": "Noman"
}
can be Base64URL encoded into a string.
Anyone can decode it.
There is no secret required to decode Base64URL.
Therefore, do not put information such as:
Password Credit card number Private keys API secrets Highly confidential information
inside an ordinary JWT payload.
A Complete JWT Example
Suppose a user logs into:
https://example.com/login
They submit:
{
"username": "noman",
"password": "********"
}
The server verifies the credentials.
The server then creates claims:
{
"sub": "1001",
"name": "Noman",
"role": "admin",
"iat": 1720000000,
"exp": 1720003600
}
The server signs the token.
The resulting JWT might look like:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIxMDAxIiwibmFtZSI6Ik5vbWFuIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzIwMDAwMDAwLCJleHAiOjE3MjAwMDM2MDB9. SIGNATURE
The client stores the token according to the application's security architecture.
For subsequent API requests:
Authorization: Bearer <JWT>
The API validates the token before processing the request.
JWT Authentication Flow
A typical JWT authentication system works as follows:
User | | Username + Password v Authentication Server | | Verify Credentials v Generate JWT | v Client | | Authorization: Bearer JWT v API Server | | Verify Signature | Verify exp | Verify iss/aud where applicable v Application
Let's break this down.
Step 1 — Login
The user submits credentials.
Step 2 — Authentication
The server verifies the credentials.
Step 3 — Token Generation
The server creates a JWT.
Step 4 — Token Delivery
The token is returned to the client.
Step 5 — API Request
The client sends:
Authorization: Bearer eyJ...
Step 6 — Verification
The server verifies the token.
Step 7 — Authorization
The application determines whether the user has permission to perform the requested operation.
JWT Signing Algorithms
JWT can use different cryptographic algorithms.
HS256
HS256 uses a shared secret.
Client <---- shared secret ----> Server
The same secret is used to create and verify the signature.
Advantages include simplicity and performance.
The main security concern is protecting the shared secret.
RS256
RS256 uses asymmetric cryptography.
There are two keys:
Private Key Public Key
The issuer signs using the private key.
The recipient verifies using the public key.
Conceptually:
Private Key
|
v
Sign
|
JWT
|
v
Public Key
|
v
Verify
This is particularly useful when many services need to verify tokens but should not possess the signing private key.
ES256
ES256 uses elliptic-curve cryptography.
It can provide strong security with relatively small keys and signatures, although implementation and key management must be handled correctly.
JWT Encryption
A standard signed JWT does not encrypt the payload.
If confidentiality is required, the JOSE ecosystem provides JWE — JSON Web Encryption.
A simplified distinction is:
JWS: Integrity + Authenticity JWE: Confidentiality + Integrity
A signed JWT answers:
Has this token been modified, and was it signed by the expected key?
An encrypted token answers:
Can someone other than the intended recipient read the information?
These are different security requirements.
In some architectures, information can be both encrypted and signed, depending on the requirements.
"Decrypting" a Normal JWT
Technically, calling it "decrypting" is incorrect for an ordinary JWT.
You normally decode it.
For example, a payload such as:
{
"sub": "1001",
"name": "Noman",
"role": "admin"
}
is Base64URL encoded.
The encoded payload can be decoded without knowing the signing secret.
The important distinction is:
Decode → Can reveal payload Verify → Confirms authenticity/integrity Decrypt → Reveals encrypted content
These are three different operations.
Example: Creating JWT in PHP
PHP applications commonly use JWT libraries.
Conceptually, code might look like:
$payload = [
'sub' => 1001,
'name' => 'Noman',
'role' => 'admin',
'iat' => time(),
'exp' => time() + 3600
];
$token = JWT::encode(
$payload,
$secretKey,
'HS256'
);
The generated token can then be returned to the client.
Example: Verifying JWT in PHP
The API receives:
Authorization: Bearer <token>
The application extracts the token and verifies its signature.
Conceptually:
$decoded = JWT::decode(
$token,
new Key($secretKey, 'HS256')
);
If the token has been modified, uses the wrong signing key, or violates relevant validation requirements, verification should fail.
Example: JWT with Node.js
A Node.js application can use a JWT library to generate tokens.
Conceptually:
const token = jwt.sign(
{
sub: "1001",
role: "admin"
},
process.env.JWT_SECRET,
{
expiresIn: "1h"
}
);
Verification:
const decoded = jwt.verify(
token,
process.env.JWT_SECRET
);
The server should validate more than merely whether the token can be decoded.
JWT and REST APIs
JWT became especially popular with REST APIs.
For example:
POST /api/login
returns:
{
"access_token": "eyJ..."
}
The client then calls:
GET /api/users
with:
Authorization: Bearer eyJ...
The API validates the token and processes the request.
This approach works well with:
- Mobile applications
- JavaScript applications
- Backend services
- Microservices
- Third-party APIs
- Distributed applications
JWT in Microservices
JWT can be particularly useful in a microservice architecture.
Imagine:
Authentication Service
|
| JWT
v
+--------------------+--------------------+
| | |
v v v
User API Payment API Order API
Each service can verify the token rather than requiring every request to return to the authentication service for basic token validation.
However, authorization decisions can still require centralized policy or additional service-to-service checks.
Access Tokens and Refresh Tokens
Many modern authentication systems use two types of tokens:
Access Token
Used to access APIs.
It is generally short-lived.
For example:
15 minutes 30 minutes 1 hour
Refresh Token
Used to obtain a new access token.
It generally has a longer lifetime and requires stronger protection.
Conceptually:
Login
|
+---- Access Token
|
+---- Refresh Token
Access Token expires
|
v
Refresh Token
|
v
New Access Token
Using short-lived access tokens can reduce the impact of token theft.
JWT Storage
Where the token is stored is an important security decision.
Common possibilities include:
- Secure, HttpOnly cookies
- In-memory application state
- Browser storage mechanisms
- Mobile secure storage
There is no universally correct storage mechanism for every architecture.
For browser applications, cookies with appropriate security attributes can help reduce exposure to JavaScript-based token theft, while applications must also consider CSRF protections and other browser security concerns.
Applications using browser storage must carefully consider XSS because malicious JavaScript may be able to access stored tokens.
Common JWT Security Mistakes
1. Putting Passwords in the Payload
Never do this:
{
"username": "noman",
"password": "mypassword"
}
JWT payloads are not automatically confidential.
2. Using Very Long-Lived Access Tokens
A token valid for months increases the potential impact if it is stolen.
Shorter access-token lifetimes can reduce exposure.
3. Not Validating Expiration
The server should validate relevant time-based claims such as exp.
4. Trusting the Payload Without Verification
Never simply decode:
JWT → Payload → Trust
Instead:
JWT | v Verify signature | v Validate claims | v Authorize request
5. Weak Secrets
If HS256 is used, the signing secret needs appropriate strength and protection.
Avoid:
secret 123456 password jwtsecret
Use a strong randomly generated secret.
6. Accepting Any Algorithm
Applications should explicitly configure which algorithms are permitted.
Do not blindly trust the algorithm indicated by an untrusted token header.
7. Ignoring Audience and Issuer
In systems where they are relevant, validate:
iss aud
This helps ensure that a token was issued by the expected authority and intended for the expected service.
JWT Revocation
One challenge with JWT is revocation.
Suppose a token is valid for:
1 hour
but the user's account is disabled after 10 minutes.
A purely stateless validation process may still consider the token cryptographically valid until it expires.
Solutions include:
- Short access-token lifetime
- Refresh-token rotation
- Token deny lists
- Token versioning
- Centralized session state
- Checking account status
jti-based revocation mechanisms
The appropriate approach depends on the application's security requirements.
JWT vs Traditional Sessions
JWT and session-based authentication solve similar problems but use different architectures.
| FeatureJWTTraditional Session | ||
| Authentication state | Often client-held token | Usually server-side |
| Server storage | Can be stateless | Usually required |
| Revocation | More complicated | Usually simpler |
| Distributed systems | Convenient | Requires shared session storage or routing |
| Token contents | Readable if only signed | Server-side data stays server-side |
| Scaling | Can simplify horizontal scaling | Often needs shared session infrastructure |
| Security | Depends heavily on implementation | Mature and well understood |
Neither approach is automatically appropriate for every application.
JWT vs OAuth 2.0
Another common misunderstanding is treating JWT and OAuth as competing technologies.
They are different things.
JWT is a token format.
OAuth 2.0 is an authorization framework.
OAuth 2.0 can use JWT-formatted access tokens, but OAuth does not require every access token to be a JWT.
Similarly, OpenID Connect builds an identity layer on top of OAuth 2.0 and commonly uses JWTs for ID tokens.
JWT and OpenID Connect
OpenID Connect, commonly abbreviated as OIDC, uses JWT extensively.
An OIDC ID token contains claims about authentication and the authenticated user.
For example:
{
"iss": "https://identity.example.com",
"sub": "123456",
"aud": "my-client",
"exp": 1720003600,
"iat": 1720000000
}
The application validates the token before relying on its claims.
Advantages of JWT
JWT has several important advantages.
Compact
JWTs are relatively compact and can be transmitted easily.
Portable
They can be used across programming languages and platforms.
URL Safe
JWT uses Base64URL encoding.
Distributed-System Friendly
Services can independently verify signed tokens when appropriate.
Flexible
Custom claims can carry application-specific information.
Standardized
JWT is defined by an IETF standard.
Disadvantages of JWT
JWT also has limitations.
Revocation Can Be Difficult
Once issued, a token can remain valid until expiration unless additional controls exist.
Tokens Can Become Large
Putting many claims into a token increases request size.
Payload Is Not Secret
A signed JWT can be decoded.
Key Management Matters
Security depends heavily on protecting signing keys.
Stateless Does Not Mean Automatically Secure
JWT can remove some server-side session state, but it does not eliminate authentication and authorization security requirements.
JWT in Real-World Applications
JWT can be found in many types of systems, including:
- REST APIs
- Mobile applications
- Single-page applications
- SaaS platforms
- Microservices
- Enterprise applications
- Identity providers
- API gateways
- Distributed applications
- Service-to-service authentication
For example, an enterprise application might issue a token containing:
{
"sub": "50025",
"role": "manager",
"department": "finance",
"scope": "invoice:read invoice:write",
"iss": "https://identity.company.com",
"aud": "finance-api",
"iat": 1720000000,
"exp": 1720003600
}
The API can then validate the token and determine whether the requested operation is authorized.
A Simple Mental Model of JWT
The easiest way to understand JWT is to remember these three concepts:
HEADER "What algorithm and token type?" PAYLOAD "What claims are being carried?" SIGNATURE "Can I trust that these contents were not modified?"
And remember:
Encoding ≠ Encryption Decoding ≠ Verification Authentication ≠ Authorization JWT ≠ OAuth
These distinctions eliminate many common JWT misunderstandings.
Recommended JWT Security Checklist
Before deploying JWT in production, consider the following checklist:
- Use HTTPS/TLS.
- Use strong signing keys.
- Protect private keys and shared secrets.
- Use short-lived access tokens where appropriate.
- Validate
exp. - Validate
nbfwhere applicable. - Validate
isswhen applicable. - Validate
audwhen applicable. - Restrict accepted algorithms.
- Do not put passwords in JWTs.
- Do not put unnecessary sensitive information in payloads.
- Protect refresh tokens carefully.
- Consider refresh-token rotation.
- Implement appropriate revocation mechanisms when required.
- Protect browser applications against XSS and CSRF according to the selected token-storage architecture.
- Log authentication events without logging sensitive tokens unnecessarily.
- Rotate signing keys according to your operational/security requirements.
- Use a well-maintained JWT library rather than implementing cryptographic primitives yourself.
Conclusion
JSON Web Token has become an important technology for modern authentication and authorization systems. Its standardized, compact, and portable format makes it particularly useful for APIs, mobile applications, single-page applications, microservices, and distributed architectures.
The most important thing to understand about JWT is that a normal JWT is generally signed, not encrypted. Its header and payload can be decoded by anyone who possesses the token. The signature provides integrity and authenticity when properly verified.
A JWT consists of:
Header.Payload.Signature
The header identifies the token and signing algorithm, the payload contains claims, and the signature protects the token from unauthorized modification.
JWT can significantly simplify authentication architectures, especially in distributed environments, but it is not a magic security solution. Token expiration, storage, key management, revocation, algorithm restrictions, audience validation, issuer validation, and authorization must all be considered carefully.
Understanding the difference between encoding, signing, verification, and encryption is particularly important. If confidential information needs to be protected, a normal signed JWT should not be treated as an encrypted container; appropriate encryption mechanisms such as JWE or another suitable secure channel should be considered.
Ultimately, JWT is best viewed as a standardized way of representing and securely carrying claims, rather than simply as a "login token." When implemented with proper cryptographic practices, secure transport, appropriate lifetimes, and robust validation, JWT can form an important component of a modern identity and API security architecture.