JJWT Implementation module providing concrete implementations of JSON Web Token (JWT) creation, parsing, verification, and cryptographic operations for Java and Android applications.
—
The JWT Parsing functionality in JJWT Implementation centers around the DefaultJwtParser and DefaultJwtParserBuilder classes, providing comprehensive parsing, validation, and verification capabilities for JWT, JWS, and JWE tokens. The parser supports flexible configuration for security policies, key resolution, and validation rules.
The configurable factory for creating JWT parsers with specific validation and security requirements.
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.JwtParserBuilder;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwe;
import io.jsonwebtoken.Locator;
import io.jsonwebtoken.Clock;
import javax.crypto.SecretKey;
import java.security.PublicKey;
import java.security.PrivateKey;
import java.security.Provider;
import java.util.Date;
// Basic parser creation
JwtParserBuilder parserBuilder = Jwts.parser();
// Parser with verification key
JwtParser parser = Jwts.parser()
.verifyWith(secretKey)
.build();
// Parser with decryption key
JwtParser decryptParser = Jwts.parser()
.decryptWith(privateKey)
.build();The main parsing engine that handles JWT, JWS, and JWE token processing.
import io.jsonwebtoken.impl.DefaultJwtParser;
// Create parser instance
JwtParser parser = Jwts.parser()
.verifyWith(verificationKey)
.build();
// Token type detection
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...";
boolean isSigned = parser.isSigned(token);
// Generic parsing (returns appropriate type based on token)
Object result = parser.parse(token);// Parse unsecured JWT with claims
JwtParser unsecuredParser = Jwts.parser()
.unsecured() // Allow unsecured JWTs
.build();
String unsecuredToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0...";
Jwt<Claims> jwt = unsecuredParser.parseClaimsJwt(unsecuredToken);
Claims claims = jwt.getPayload();
String subject = claims.getSubject();
String issuer = claims.getIssuer();
Date expiration = claims.getExpiration();
// Parse unsecured JWT with content payload
Jwt<String> contentJwt = unsecuredParser.parseContentJwt(contentToken);
String content = contentJwt.getPayload();// HMAC signature verification
SecretKey hmacKey = getHmacKey();
JwtParser hmacParser = Jwts.parser()
.verifyWith(hmacKey)
.build();
String signedToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...";
Jws<Claims> jws = hmacParser.parseSignedClaims(signedToken);
Claims verifiedClaims = jws.getPayload();
// Access signature information
String algorithm = jws.getHeader().getAlgorithm();
String keyId = jws.getHeader().getKeyId();
// RSA signature verification
PublicKey rsaPublicKey = getRsaPublicKey();
JwtParser rsaParser = Jwts.parser()
.verifyWith(rsaPublicKey)
.build();
Jws<Claims> rsaJws = rsaParser.parseSignedClaims(rsaSignedToken);
// EC signature verification
PublicKey ecPublicKey = getEcPublicKey();
JwtParser ecParser = Jwts.parser()
.verifyWith(ecPublicKey)
.build();
Jws<Claims> ecJws = ecParser.parseSignedClaims(ecSignedToken);// Direct decryption
SecretKey contentKey = getContentEncryptionKey();
JwtParser directParser = Jwts.parser()
.decryptWith(contentKey)
.build();
String encryptedToken = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0...";
Jwe<Claims> jwe = directParser.parseEncryptedClaims(encryptedToken);
Claims decryptedClaims = jwe.getPayload();
// Key wrapping decryption
SecretKey keyEncryptionKey = getKeyEncryptionKey();
JwtParser kwParser = Jwts.parser()
.decryptWith(keyEncryptionKey)
.build();
Jwe<Claims> kwJwe = kwParser.parseEncryptedClaims(keyWrappedToken);
// RSA decryption
PrivateKey rsaPrivateKey = getRsaPrivateKey();
JwtParser rsaDecryptParser = Jwts.parser()
.decryptWith(rsaPrivateKey)
.build();
Jwe<Claims> rsaJwe = rsaDecryptParser.parseEncryptedClaims(rsaEncryptedToken);import io.jsonwebtoken.Locator;
import io.jsonwebtoken.ProtectedHeader;
// Dynamic key resolution based on header information
Locator<Key> keyLocator = new Locator<Key>() {
@Override
public Key locate(ProtectedHeader header) {
String keyId = header.getKeyId();
String algorithm = header.getAlgorithm();
if ("HS256".equals(algorithm)) {
return getHmacKeyById(keyId);
} else if ("RS256".equals(algorithm)) {
return getRsaPublicKeyById(keyId);
} else if ("ES256".equals(algorithm)) {
return getEcPublicKeyById(keyId);
}
throw new SecurityException("Unsupported algorithm: " + algorithm);
}
};
JwtParser dynamicParser = Jwts.parser()
.keyLocator(keyLocator)
.build();
// Parse with dynamic key resolution
Jws<Claims> dynamicJws = dynamicParser.parseSignedClaims(tokenWithKeyId);import io.jsonwebtoken.Clock;
import java.time.Instant;
// Custom clock for testing or specific time zones
Clock customClock = () -> Date.from(Instant.parse("2024-01-01T12:00:00Z"));
JwtParser clockParser = Jwts.parser()
.verifyWith(secretKey)
.clock(customClock)
.build();
// Clock skew tolerance
JwtParser tolerantParser = Jwts.parser()
.verifyWith(secretKey)
.clockSkewSeconds(300) // Allow 5 minutes clock skew
.build();
Jws<Claims> tolerantJws = tolerantParser.parseSignedClaims(tokenWithTimeSkew);// Configure supported signature algorithms
JwtParser sigParser = Jwts.parser()
.verifyWith(secretKey)
.sig() // Access signature algorithm registry
.add(customSignatureAlgorithm)
.and()
.build();
// Configure supported encryption algorithms
JwtParser encParser = Jwts.parser()
.decryptWith(decryptionKey)
.enc() // Access encryption algorithm registry
.add(customEncryptionAlgorithm)
.and()
.key() // Access key algorithm registry
.add(customKeyAlgorithm)
.and()
.build();
// Configure compression algorithms
JwtParser zipParser = Jwts.parser()
.verifyWith(secretKey)
.zip() // Access compression algorithm registry
.add(customCompressionAlgorithm)
.and()
.build();The JwtParserBuilder provides comprehensive methods for requiring specific claim values during parsing.
// Require specific standard claims
JwtParser requiredClaimsParser = Jwts.parser()
.verifyWith(secretKey)
.requireId("unique-token-id") // Require specific 'jti' claim
.requireSubject("john.doe") // Require specific 'sub' claim
.requireIssuer("https://auth.example.com") // Require specific 'iss' claim
.requireAudience("api.example.com") // Require specific 'aud' claim
.requireIssuedAt(specificIssuedAtDate) // Require specific 'iat' claim
.requireExpiration(specificExpirationDate) // Require specific 'exp' claim
.requireNotBefore(specificNotBeforeDate) // Require specific 'nbf' claim
.build();
// Require custom claims
JwtParser customClaimsParser = Jwts.parser()
.verifyWith(secretKey)
.require("role", "admin") // Require custom claim value
.require("tenant_id", "acme-corp") // Require tenant identification
.require("scope", "read:users") // Require specific scope
.require("version", 2) // Require numeric claim value
.build();
// Mixed requirements
JwtParser mixedParser = Jwts.parser()
.verifyWith(secretKey)
.requireIssuer("trusted-issuer")
.requireSubject("service-account")
.require("service_type", "background")
.require("priority", "high")
.clockSkewSeconds(60) // Also configure clock skew
.build();
// Parse with automatic validation
try {
Jws<Claims> validatedJws = requiredClaimsParser.parseSignedClaims(token);
// Token automatically validated against all requirements
Claims validatedClaims = validatedJws.getPayload();
processValidatedToken(validatedClaims);
} catch (MissingClaimException e) {
// Required claim is missing
handleMissingClaim(e.getClaimName());
} catch (IncorrectClaimException e) {
// Claim has incorrect value
handleIncorrectClaim(e.getClaimName(), e.getExpectedValue(), e.getActualValue());
}Complete configuration of supported algorithms for parsing operations.
import io.jsonwebtoken.security.MacAlgorithm;
import io.jsonwebtoken.security.SignatureAlgorithm;
import io.jsonwebtoken.security.AeadAlgorithm;
import io.jsonwebtoken.security.KeyAlgorithm;
import io.jsonwebtoken.io.CompressionAlgorithm;
// Configure signature algorithms collection
JwtParser signatureParser = Jwts.parser()
.verifyWith(secretKey)
.sig()
.add(Jwts.SIG.HS256) // Add specific MAC algorithm
.add(Jwts.SIG.HS384) // Add another MAC algorithm
.add(Jwts.SIG.RS256) // Add RSA signature algorithm
.remove(Jwts.SIG.NONE) // Remove unsecured algorithm
.and()
.build();
// Configure encryption algorithms collection
JwtParser encryptionParser = Jwts.parser()
.decryptWith(decryptionKey)
.enc()
.add(Jwts.ENC.A256GCM) // Add AEAD encryption algorithm
.add(Jwts.ENC.A192GCM) // Add another encryption algorithm
.and()
.key()
.add(Jwts.KEY.A256KW) // Add key wrapping algorithm
.add(Jwts.KEY.DIRECT) // Add direct key algorithm
.and()
.build();
// Configure compression algorithms collection
JwtParser compressionParser = Jwts.parser()
.verifyWith(secretKey)
.zip()
.add(Jwts.ZIP.DEF) // Add DEFLATE compression
.add(Jwts.ZIP.GZIP) // Add GZIP compression
.and()
.build();
// Comprehensive algorithm configuration
JwtParser comprehensiveParser = Jwts.parser()
.verifyWith(verificationKey)
.decryptWith(decryptionKey)
.sig()
.add(Jwts.SIG.HS512) // Strong HMAC
.add(Jwts.SIG.RS256) // RSA signature
.add(Jwts.SIG.ES256) // Elliptic curve signature
.and()
.enc()
.add(Jwts.ENC.A256GCM) // Strong encryption
.and()
.key()
.add(Jwts.KEY.A256KW) // Key wrapping
.add(Jwts.KEY.RSA_OAEP) // RSA key encryption
.and()
.zip()
.add(Jwts.ZIP.DEF) // Compression support
.and()
.clockSkewSeconds(30) // Time tolerance
.build();Configuration for handling unsecured JWTs in controlled scenarios.
// Allow parsing of unsecured JWTs (use with extreme caution)
JwtParser unsecuredParser = Jwts.parser()
.unsecured() // Allow parsing without signature verification
.build();
// Parse unsecured JWT
Jwt<Header, Claims> unsecuredJwt = unsecuredParser.parseUnsecuredClaims(unsecuredToken);
Claims unsecuredClaims = unsecuredJwt.getPayload();
// Allow decompression of unsecured content (additional risk)
JwtParser unsecuredDecompressionParser = Jwts.parser()
.unsecured()
.unsecuredDecompression() // Allow decompression without verification
.build();
// Production pattern: Conditional unsecured parsing
public JwtParser createParser(boolean allowUnsecured, SecretKey key) {
JwtParserBuilder builder = Jwts.parser();
if (allowUnsecured) {
builder.unsecured();
// Log security warning
log.warn("SECURITY: Unsecured JWT parsing enabled");
} else {
builder.verifyWith(key);
}
return builder.build();
}// Define critical header parameter handlers
JwtParser criticalParser = Jwts.parser()
.verifyWith(secretKey)
.critical() // Configure critical header handling
.add("custom-critical-param")
.add("security-level")
.and()
.build();
// Parser will validate that tokens include handlers for critical parameters
Jws<Claims> criticalJws = criticalParser.parseSignedClaims(tokenWithCriticalHeaders);import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.PrematureJwtException;
JwtParser timeParser = Jwts.parser()
.verifyWith(secretKey)
.clockSkewSeconds(30) // 30 seconds tolerance
.build();
try {
Jws<Claims> validJws = timeParser.parseSignedClaims(token);
Claims claims = validJws.getPayload();
// Token is valid and within time bounds
processValidToken(claims);
} catch (ExpiredJwtException e) {
// Token has expired
handleExpiredToken(e);
} catch (PrematureJwtException e) {
// Token is not yet valid (nbf claim)
handlePrematureToken(e);
}import io.jsonwebtoken.security.SignatureException;
import io.jsonwebtoken.MalformedJwtException;
try {
Jws<Claims> validJws = parser.parseSignedClaims(suspiciousToken);
} catch (SignatureException e) {
// Signature verification failed
logSecurityIncident("Invalid signature", e);
throw new SecurityException("Token signature invalid");
} catch (MalformedJwtException e) {
// Token structure is malformed
logSecurityIncident("Malformed token", e);
throw new IllegalArgumentException("Invalid token format");
}import io.jsonwebtoken.security.SecurityException;
try {
Jwe<Claims> decryptedJwe = encryptParser.parseEncryptedClaims(encryptedToken);
} catch (SecurityException e) {
// Decryption failed or key mismatch
handleDecryptionFailure(e);
} catch (Exception e) {
// Other encryption-related errors
handleGeneralEncryptionError(e);
}// Parse JWT with string content
JwtParser contentParser = Jwts.parser()
.verifyWith(secretKey)
.build();
Jws<String> contentJws = contentParser.parseSignedContent(signedContentToken);
String content = contentJws.getPayload();
// Handle different content types based on header
String contentType = contentJws.getHeader().getContentType();
if ("application/json".equals(contentType)) {
JsonObject json = parseJson(content);
} else if ("text/plain".equals(contentType)) {
String plainText = content;
}// Parse JWT with binary content
Jws<byte[]> binaryJws = contentParser.parseSignedContent(binaryContentToken);
byte[] binaryContent = binaryJws.getPayload();
// Process based on content type
String contentType = binaryJws.getHeader().getContentType();
if ("application/pdf".equals(contentType)) {
processPdfContent(binaryContent);
} else if ("image/jpeg".equals(contentType)) {
processImageContent(binaryContent);
}import java.util.List;
// Support multiple verification keys for key rotation
List<SecretKey> rotationKeys = Arrays.asList(currentKey, previousKey, oldKey);
Locator<Key> rotationLocator = header -> {
String keyId = header.getKeyId();
// Try keys in order of preference
for (SecretKey key : rotationKeys) {
if (keyMatches(key, keyId)) {
return key;
}
}
throw new SecurityException("No valid key found for keyId: " + keyId);
};
JwtParser rotationParser = Jwts.parser()
.keyLocator(rotationLocator)
.build();// Tenant-specific key resolution
Locator<Key> tenantKeyLocator = header -> {
String keyId = header.getKeyId();
String tenantId = extractTenantFromKeyId(keyId);
return getTenantVerificationKey(tenantId, keyId);
};
JwtParser tenantParser = Jwts.parser()
.keyLocator(tenantKeyLocator)
.build();
// Parse tenant-specific tokens
Jws<Claims> tenantJws = tenantParser.parseSignedClaims(tenantToken);
String tenantId = tenantJws.getPayload().get("tenant_id", String.class);import java.security.Provider;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
// Use specific JCA provider
Provider bcProvider = new BouncyCastleProvider();
JwtParser providerParser = Jwts.parser()
.provider(bcProvider)
.verifyWith(secretKey)
.build();
Jws<Claims> bcJws = providerParser.parseSignedClaims(token);import io.jsonwebtoken.io.Deserializer;
import io.jsonwebtoken.jackson.io.JacksonDeserializer;
// Custom JSON deserializer
Deserializer<Map<String, ?>> deserializer = new JacksonDeserializer<>();
JwtParser jsonParser = Jwts.parser()
.json(deserializer)
.verifyWith(secretKey)
.build();
Jws<Claims> customJws = jsonParser.parseSignedClaims(tokenWithCustomJson);// Create parser once and reuse (thread-safe)
JwtParser reusableParser = Jwts.parser()
.verifyWith(secretKey)
.clockSkewSeconds(30)
.build();
// Reuse across multiple parsing operations
public Claims parseUserToken(String token) {
return reusableParser.parseSignedClaims(token).getPayload();
}
public Claims parseServiceToken(String token) {
return reusableParser.parseSignedClaims(token).getPayload();
}// Process multiple tokens efficiently
List<String> tokens = getTokenBatch();
JwtParser batchParser = Jwts.parser()
.keyLocator(efficientKeyLocator)
.build();
List<Claims> claimsList = tokens.stream()
.map(token -> {
try {
return batchParser.parseSignedClaims(token).getPayload();
} catch (Exception e) {
logParsingError(token, e);
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());// Allow both secured and unsecured JWTs
JwtParser flexibleParser = Jwts.parser()
.unsecured() // Allow unsecured JWTs
.verifyWith(secretKey) // But verify if signature present
.build();
// Parse any type of JWT
Object result = flexibleParser.parse(unknownToken);
if (result instanceof Jws) {
Jws<Claims> jws = (Jws<Claims>) result;
Claims verifiedClaims = jws.getPayload();
} else if (result instanceof Jwt) {
Jwt<Claims> jwt = (Jwt<Claims>) result;
Claims unverifiedClaims = jwt.getPayload();
}// Allow decompression for unsecured JWTs (use with caution)
JwtParser decompressParser = Jwts.parser()
.unsecured()
.unsecuredDecompression() // Allow decompression of unsecured JWTs
.build();
Jwt<Claims> decompressedJwt = decompressParser.parseClaimsJwt(compressedUnsecuredToken);The JJWT library provides a comprehensive exception hierarchy for handling various JWT parsing and validation errors.
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.PrematureJwtException;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.InvalidClaimException;
import io.jsonwebtoken.MissingClaimException;
import io.jsonwebtoken.IncorrectClaimException;
import io.jsonwebtoken.RequiredTypeException;
import io.jsonwebtoken.security.SecurityException;
import io.jsonwebtoken.security.SignatureException;
/**
* Base class for JWT-related runtime exceptions.
*/
public class JwtException extends RuntimeException;
/**
* Exception indicating that a JWT string is not in the proper three-part format.
*/
public class MalformedJwtException extends JwtException;
/**
* Exception indicating that a JWT is expired and therefore not valid.
*/
public class ExpiredJwtException extends ClaimJwtException;
/**
* Exception indicating that a JWT is not yet valid (nbf claim).
*/
public class PrematureJwtException extends ClaimJwtException;
/**
* Exception indicating an unsupported JWT type or algorithm.
*/
public class UnsupportedJwtException extends JwtException;
/**
* Exception indicating a claim validation failure.
*/
public class InvalidClaimException extends ClaimJwtException;
/**
* Exception indicating a required claim is missing.
*/
public class MissingClaimException extends InvalidClaimException;
/**
* Exception indicating a claim has an incorrect value.
*/
public class IncorrectClaimException extends InvalidClaimException;
/**
* Exception indicating incorrect type conversion.
*/
public class RequiredTypeException extends JwtException;
/**
* Base class for security-related exceptions.
*/
public class SecurityException extends JwtException;
/**
* Exception indicating JWT signature verification failure.
*/
public class SignatureException extends SecurityException;// Comprehensive error handling
public Claims parseAndValidateToken(String token, SecretKey key) {
JwtParser parser = Jwts.parser()
.verifyWith(key)
.build();
try {
Jws<Claims> jws = parser.parseSignedClaims(token);
return jws.getPayload();
} catch (ExpiredJwtException e) {
// Token has expired
Date expiration = e.getClaims().getExpiration();
throw new TokenExpiredException("Token expired at: " + expiration);
} catch (PrematureJwtException e) {
// Token not yet valid
Date notBefore = e.getClaims().getNotBefore();
throw new TokenNotYetValidException("Token not valid until: " + notBefore);
} catch (MalformedJwtException e) {
// Token format is invalid
throw new InvalidTokenFormatException("Invalid JWT format: " + e.getMessage());
} catch (SignatureException e) {
// Signature verification failed
throw new InvalidSignatureException("JWT signature verification failed");
} catch (MissingClaimException e) {
// Required claim is missing
String claimName = e.getClaimName();
throw new MissingRequiredClaimException("Missing required claim: " + claimName);
} catch (IncorrectClaimException e) {
// Claim has wrong value
String claimName = e.getClaimName();
Object expectedValue = e.getExpectedValue();
Object actualValue = e.getActualValue();
throw new InvalidClaimValueException(
"Claim '" + claimName + "' expected '" + expectedValue +
"' but was '" + actualValue + "'");
} catch (UnsupportedJwtException e) {
// Unsupported JWT type
throw new UnsupportedTokenException("Unsupported JWT: " + e.getMessage());
} catch (RequiredTypeException e) {
// Type conversion failed
throw new TypeConversionException("Failed to convert claim type: " + e.getMessage());
} catch (JwtException e) {
// General JWT error
throw new TokenProcessingException("JWT processing error: " + e.getMessage());
}
}// Handle specific validation scenarios
public void handleExpirationWithGracePeriod(String token, SecretKey key) {
try {
JwtParser parser = Jwts.parser()
.verifyWith(key)
.clockSkewSeconds(30) // 30 second grace period
.build();
Jws<Claims> jws = parser.parseSignedClaims(token);
processValidToken(jws.getPayload());
} catch (ExpiredJwtException e) {
Claims expiredClaims = e.getClaims();
Date expiration = expiredClaims.getExpiration();
long gracePeriodMs = 60 * 1000; // 1 minute additional grace
if (System.currentTimeMillis() - expiration.getTime() < gracePeriodMs) {
// Within extended grace period
processValidToken(expiredClaims);
} else {
throw new TokenDefinitelyExpiredException("Token expired beyond grace period");
}
}
}
// Extract claims from expired tokens for logging
public void logTokenDetails(String token) {
try {
// This will throw ExpiredJwtException but still provide claims
Jwts.parser().verifyWith(key).build().parseSignedClaims(token);
} catch (ExpiredJwtException e) {
// Can still access claims from expired token
Claims claims = e.getClaims();
String subject = claims.getSubject();
Date expiration = claims.getExpiration();
log.info("Expired token - Subject: {}, Expired: {}", subject, expiration);
} catch (JwtException e) {
log.error("Token parsing failed: {}", e.getMessage());
}
}// Custom exception handler for multi-tenant scenarios
public class TenantAwareJwtHandler {
public Claims parseToken(String token, String tenantId) {
try {
SecretKey tenantKey = getTenantKey(tenantId);
JwtParser parser = Jwts.parser()
.verifyWith(tenantKey)
.require("tenant_id", tenantId) // Require specific tenant
.build();
return parser.parseSignedClaims(token).getPayload();
} catch (IncorrectClaimException e) {
if ("tenant_id".equals(e.getClaimName())) {
throw new InvalidTenantException(
"Token belongs to different tenant: " + e.getActualValue());
}
throw e;
} catch (MissingClaimException e) {
if ("tenant_id".equals(e.getClaimName())) {
throw new InvalidTenantException("Token missing tenant claim");
}
throw e;
}
}
private SecretKey getTenantKey(String tenantId) {
// Retrieve tenant-specific key
return tenantKeyRepository.getKey(tenantId);
}
}The JWT Parsing functionality provides comprehensive, secure, and flexible parsing capabilities with extensive validation options, error handling, and performance optimizations for production use cases.
Install with Tessl CLI
npx tessl i tessl/maven-io-jsonwebtoken--jjwt-impl