Understanding JWT: Pros, Cons, and Best Security Practices


What is JWT (JSON Web Token)?
Imagine you're handing a VIP pass to someone for a concert. This pass grants them access to specific areas and activities. In the digital world, JSON Web Token (JWT) works similarly. It's an open standard (RFC 7519) that securely transmits information between parties as a JSON object. This information can be verified and trusted because it is digitally signed using a secret (HMAC) or a public/private key pair (RSA/ECDSA).
JWTs are commonly used in authentication and authorization mechanisms, allowing users to authenticate once and receive a token that grants access to protected resources.
Structure of a JWT
A JWT consists of three parts, separated by dots (.
):
<header>.<payload>.<signature>
1. Header
Contains metadata about the token, including the type of token (JWT
) and the signing algorithm (HS256
, RS256
, etc.).
{
"alg": "HS256",
"typ": "JWT"
}
2. Payload
Contains claims (data) about the user, such as their user ID, role, and expiration time.
{
"sub": "1234567890",
"name": "John Doe",
"role": "admin",
"iat": 1708275200, // Issued At
"exp": 1708361600 // Expiration time
}
3. Signature
The signature is created by signing the encoded header and encoded payload using a secret key or a private key.
For HMAC (HS256) algorithm:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
When a client sends a JWT to a server, the server verifies the signature before processing the request.
🔐 Complete JWT Token
Combining all three parts (header.payload.signature):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzA4Mjc1MjAwLCJleHAiOjE3MDgzNjE2MDB9.
uPMQ1yRyUzv1UKX2RfBdOwzMg2FajRMiWBJDtx7x3kM
⚙️ Step-by-Step JWT Authentication Flow
🧾 Example: User Login Process
Scenario: A user logs into a web application.
Step 1: User Logs In
The user provides valid credentials (username
& password
).
Request:
httpCopyEditPOST /api/login
Content-Type: application/json
{
"username": "john.doe",
"password": "mypassword123"
}
Step 2: Server Validates Credentials
The server checks the username and password against its database.
If valid, the server generates a JWT like this:
JWT Payload:
jsonCopyEdit{
"sub": "user123",
"name": "John Doe",
"role": "admin",
"iat": 1708275200,
"exp": 1708361600
}
Server-side Code (Node.js with jsonwebtoken
library):
javascriptCopyEditconst jwt = require('jsonwebtoken');
const user = { id: 'user123', name: 'John Doe', role: 'admin' };
// Secret Key (must be kept safe)
const secretKey = 'my_super_secret_key';
// Generate JWT
const token = jwt.sign(user, secretKey, { expiresIn: '1h' });
console.log('JWT Token:', token);
Response (JWT returned to the client):
jsonCopyEdit{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Step 3: Client Stores and Uses the JWT
The client stores the JWT in:
LocalStorage: For browser-based apps.
SessionStorage: For short-lived sessions.
HTTP-only cookies: For better security.
Example Header for Authenticated Requests:
hCopyEditGET /api/protected-resource
Authorization: Bearer <JWT_TOKEN>
Step 4: Server Validates the JWT
Upon receiving a protected request, the server verifies the JWT.
JWT Verification Code (Node.js):
javascriptCopyEdit// Middleware to verify JWT
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, secretKey, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
🛠️ How JWT Signature Verification Works
The server:
Decodes the token (Header & Payload).
Recomputes the signature with the same algorithm and secret key.
Compares it with the received signature.
If it matches, the token is valid.
Pros of Using JWT
1. Stateless Authentication
No need for a session store; tokens contain all required information.
Useful for microservices and distributed systems.
2. Compact and Fast
JWTs are small in size and can be easily transmitted via HTTP headers.
They perform well in web and mobile applications.
3. Secure Data Transmission
JWTs are signed, ensuring data integrity and preventing tampering.
When encrypted (JWE), they provide confidentiality.
4. Cross-Domain Authentication
- JWTs work seamlessly with Single Sign-On (SSO) systems across multiple domains.
Cons of Using JWT
1. Cannot Be Revoked Easily
Once issued, a JWT remains valid until expiration.
If stolen, an attacker can use it until the token expires.
2. Larger Than Traditional Session Tokens
- If the payload contains too much data, JWTs can become large, impacting performance.
3. Risk of Token Theft
- If an attacker gains access to a JWT, they can impersonate the user and perform malicious actions.
4. No Built-in Key Rotation
- Changing signing keys requires careful implementation to avoid breaking authentication.
Security Risks: If a JWT Is Hacked, the Hacker Can Act on Your Behalf
Imagine giving someone a signed check—if they copy it, they can withdraw money until the bank notices. One of the biggest risks with JWT is that if an attacker obtains a valid token, they can impersonate the user and make API requests on their behalf. Unlike session-based authentication, JWTs do not have a central session store where tokens can be invalidated in real-time.
Example Attack Scenarios:
Token Theft via XSS (Cross-Site Scripting): If a JWT is stored in
localStorage
, an attacker can steal it using malicious scripts.Man-in-the-Middle (MITM) Attacks: If JWTs are transmitted over an insecure HTTP connection, they can be intercepted.
Token Replay Attacks: If the same JWT is reused multiple times, an attacker can gain persistent unauthorized access.
Best Practices for Securing JWT
1. Use Short-Lived Access Tokens & Refresh Tokens
Set a short expiration time (
exp
claim) for access tokens (e.g., 15 minutes).Use refresh tokens stored securely in httpOnly cookies.
Invalidate refresh tokens if suspicious activity is detected.
2. Secure Token Storage
DO NOT store JWTs in
localStorage
orsessionStorage
(prone to XSS attacks).Use httpOnly, secure, and SameSite=strict cookies to store tokens.
3. Implement Token Blacklisting
Maintain a server-side revocation list to block compromised tokens.
Implement token versioning so that updating a user’s session invalidates old tokens.
4. Use Strong Secret Keys and Signing Algorithms
Use RS256 (asymmetric key signing) instead of HS256 (symmetric key signing) for better security.
Store private keys securely using AWS KMS, Azure Key Vault, or Google Cloud KMS.
5. Validate JWT Signature, Issuer, and Audience
Ensure tokens are signed by your trusted authentication server.
Validate the issuer (
iss
) and audience (aud
) fields to prevent accepting forged tokens.
6. Use HTTPS Everywhere
Always serve your application over HTTPS to prevent token interception.
Implement HSTS (HTTP Strict Transport Security) to enforce secure connections.
7. Implement Token Binding (Device-Specific Tokens)
Bind JWTs to specific user attributes (e.g., IP, device fingerprint, User-Agent).
Revoke tokens if access is attempted from an unknown source.
8. Monitor and Log API Access
Log authentication attempts and API requests.
Detect anomalies like multiple logins from different locations in a short time.
9. Require Reauthentication for Critical Actions
- Force users to reauthenticate for sensitive actions (e.g., password change, money transfer).
10. Use OAuth2 with JWT for Enhanced Security
Instead of using only JWT, integrate OAuth2 flows (PKCE, implicit flow) to enhance security.
Use scopes and permissions to control token access levels.
Conclusion
JWTs are a powerful tool for authentication and authorization in modern web applications, but they come with security risks. If an attacker steals a JWT, they can act on behalf of the user until the token expires.
By following best practices such as short-lived tokens, secure storage, strong encryption, HTTPS enforcement, and token blacklisting, you can significantly reduce the risks associated with JWTs and make your application more secure.
Subscribe to my newsletter
Read articles from Prashant Bale directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Prashant Bale
Prashant Bale
With 17+ years in software development and 14+ years specializing in Android app architecture and development, I am a seasoned Lead Android Developer. My comprehensive knowledge spans all phases of mobile application development, particularly within the banking domain. I excel at transforming business needs into secure, user-friendly solutions known for their scalability and durability. As a proven leader and Mobile Architect.