JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. It serves as a mechanism for authentication in HTTP communications, addressing the stateless nature of the protocol.
Unlike traditional session-based authentication where server-side storage is required, JWT enables stateless authentication by having the client store a token containing verification information. This eliminates several limitations of session-based approaches:
- Eliminates the need for server-side session storage, reducing server resource requirements
- Simplifies horizontal scaling as authentication doesn't depend on server-specific session data
- Enables easier cross-domain authentication scenarios
- How JWT Authentication Works
The JWT authentication process follows these steps:
- User submits credentials (username and password) to the server
- Server validates the credentials
- Upon successful validation, server generates a JWT and returns it to the client
- Client includes the JWT in subsequent requests (typically in the Authorization header)
- Server validates the JWT and processes the request
The client typically stores the token in browser cookies or localStorage. The server doesn't need to store tokens, only validate them, making the system stateless and more scalable.
- JWT Structure and Principles
A JWT consists of three parts separated by dots (.):
- Header
- Payload li>Signature
3.1 Header
The header is a JSON object containing metadata about the token, including the signing algorithm and token type:
{
"alg": "HS256",
"typ": "JWT"
}
This JSON object is then Base64URL-encoded to create the first part of the token.
3.2 Payload
The payload contains the claims, which are statements about an entity (typically the user) and additional data. JWT defines several standard claims:
- iss (issuer): The issuer of the token
- exp (expiration time): Expiration time of the token
- sub (subject): The subject of the token
- aud (audience): The intended audience
- nbf (not before): The time before which the token must not be accepted
- iat (issued at): The time at which the token was issued
- jti (JWT ID): A unique identifier for the token
You can also include custom claims in the payload:
{
"userId": "1234567890",
"username": "john.doe",
"isAdmin": true
}
Important: The payload is not encrypted by default, so sensitive information should not be included here. This JSON object is also Base64URL-encoded.
3.3 Signature
The signature is used to verify that the token hasn't been tampered with. It's created by:
- Taking the encoded header and payload
- Combining them with a secret key
- Applying the specified algorithm (default is HMAC SHA256)
The signature is calculated as:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
3.4 Base64URL Encoding
JWT uses Base64URL encoding instead of standard Base64. This encoding replaces: - '+' with '-' - '/' with '_' - Removes padding '=' characters
This modification makes the token safe to use in URL environments.
- JWT Implementation Example
Here's a practical implementation using Java and the JJWT library:
4.1 Project Setup
Add the following dependencies to your Maven project:
<dependencies>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
</dependencies>
4.2 Token Generation
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;
public class JwtGenerator {
private static final Key SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
private static final long EXPIRATION_TIME = 86400000; // 24 hours
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SECRET_KEY)
.compact();
}
}
4.3 Token Validation
import io.jsonwebtoken.*;
import java.security.Key;
public class JwtValidator {
private static final Key SECRET_KEY = JwtGenerator.SECRET_KEY;
public static boolean validateToken(String token) {
try {
Jwts.parserBuilder()
.setSigningKey(SECRET_KEY)
.build()
.parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
public static String extractUsername(String token) {
return Jwts.parserBuilder()
.setSigningKey(SECRET_KEY)
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
}
}
- Best Practices for JWT Implementation
- Always use HTTPS to transmit JWTs
- Set appropriate expiration times for tokens
- Implement token refresh mechanisms
- Store tokens securely (HttpOnly cookies or localStorage with proper security measures)
- Validate token signatures on every request
- Consider implementing token revocation mechanisms for critical applications
- Don't store sensitive information in the payload
- JWT Security Considerations
- JWTs are not encrypted by default, only signed
- Once issued, JWTs cannot be revoked until they expire
- Always validate the expiration time on the server side
- Implement proper token validation to prevent tampering
- Consider using short-lived tokens with refresh tokens for better security