CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-auth0--java-jwt

A comprehensive Java implementation of JSON Web Token (JWT) with creation, signing, and verification capabilities for server-side JVM applications.

Pending
Overview
Eval results
Files

key-providers.mddocs/

Key Providers

Dynamic key resolution interfaces supporting key rotation, multi-tenant scenarios, and advanced key management patterns for RSA and ECDSA algorithms.

Capabilities

Key Provider Interface

Generic interface for providing cryptographic keys with support for key identification and dynamic resolution.

/**
 * Generic key provider interface for dynamic key resolution
 * @param <U> Public key type (RSAPublicKey or ECPublicKey)
 * @param <R> Private key type (RSAPrivateKey or ECPrivateKey)
 */
public interface KeyProvider<U, R> {
    /**
     * Getter for a Public Key instance using the received Key Id.
     * @param keyId the Key Id specified in the JWT header
     * @return a public key instance for verification or null if not found
     */
    U getPublicKeyById(String keyId);
    
    /**
     * Getter for a Private Key instance used for signing JWTs.
     * @return a private key instance for signing or null if not available
     */
    R getPrivateKey();
    
    /**
     * Getter for the Id of the Private Key used for signing JWTs.
     * @return the key id of the private key or null if not specified
     */
    String getPrivateKeyId();
}

Usage Examples:

import com.auth0.jwt.interfaces.KeyProvider;
import java.security.interfaces.RSAPublicKey;
import java.security.interfaces.RSAPrivateKey;

// Custom key provider implementation
public class CustomKeyProvider implements KeyProvider<RSAPublicKey, RSAPrivateKey> {
    private Map<String, RSAPublicKey> publicKeys;
    private RSAPrivateKey privateKey;
    private String privateKeyId;
    
    public CustomKeyProvider(Map<String, RSAPublicKey> publicKeys, 
                           RSAPrivateKey privateKey, 
                           String privateKeyId) {
        this.publicKeys = publicKeys;
        this.privateKey = privateKey;
        this.privateKeyId = privateKeyId;
    }
    
    @Override
    public RSAPublicKey getPublicKeyById(String keyId) {
        return publicKeys.get(keyId);
    }
    
    @Override
    public RSAPrivateKey getPrivateKey() {
        return privateKey;
    }
    
    @Override
    public String getPrivateKeyId() {
        return privateKeyId;
    }
}

RSA Key Provider

Specialized key provider interface for RSA cryptographic operations.

/**
 * RSA Key Provider interface for providing RSA keys for JWT signing and verification.
 * Extends the generic KeyProvider with RSA-specific key types.
 */
public interface RSAKeyProvider extends KeyProvider<RSAPublicKey, RSAPrivateKey> {
    /**
     * Getter for an RSA Public Key instance using the received Key Id.
     * @param keyId the Key Id specified in the JWT header ("kid" claim)
     * @return an RSAPublicKey instance for verification or null if not found
     */
    RSAPublicKey getPublicKeyById(String keyId);
    
    /**
     * Getter for an RSA Private Key instance used for signing JWTs.
     * @return an RSAPrivateKey instance for signing or null if not available
     */
    RSAPrivateKey getPrivateKey();
    
    /**
     * Getter for the Id of the RSA Private Key used for signing JWTs.
     * @return the key id of the private key or null if not specified
     */
    String getPrivateKeyId();
}

Usage Examples:

import com.auth0.jwt.interfaces.RSAKeyProvider;
import com.auth0.jwt.algorithms.Algorithm;
import java.security.interfaces.RSAPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.util.Map;
import java.util.HashMap;

// Simple RSA key provider implementation
public class SimpleRSAKeyProvider implements RSAKeyProvider {
    private final Map<String, RSAPublicKey> publicKeys;
    private final RSAPrivateKey privateKey;
    private final String privateKeyId;
    
    public SimpleRSAKeyProvider(RSAPrivateKey privateKey, String privateKeyId, 
                              Map<String, RSAPublicKey> publicKeys) {
        this.privateKey = privateKey;
        this.privateKeyId = privateKeyId;
        this.publicKeys = new HashMap<>(publicKeys);
    }
    
    @Override
    public RSAPublicKey getPublicKeyById(String keyId) {
        return publicKeys.get(keyId);
    }
    
    @Override
    public RSAPrivateKey getPrivateKey() {
        return privateKey;
    }
    
    @Override
    public String getPrivateKeyId() {
        return privateKeyId;
    }
}

// Usage with Algorithm
Map<String, RSAPublicKey> publicKeys = new HashMap<>();
publicKeys.put("key-1", rsaPublicKey1);
publicKeys.put("key-2", rsaPublicKey2);

RSAKeyProvider keyProvider = new SimpleRSAKeyProvider(
    rsaPrivateKey, 
    "key-1", 
    publicKeys
);

Algorithm algorithm = Algorithm.RSA256(keyProvider);

// Create JWT with key ID in header
String token = JWT.create()
    .withKeyId("key-1")
    .withIssuer("auth0")
    .sign(algorithm);

// Verify JWT - key provider will be used to resolve public key by key ID
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token);

ECDSA Key Provider

Specialized key provider interface for Elliptic Curve Digital Signature Algorithm operations.

/**
 * ECDSA Key Provider interface for providing EC keys for JWT signing and verification.
 * Extends the generic KeyProvider with ECDSA-specific key types.
 */
public interface ECDSAKeyProvider extends KeyProvider<ECPublicKey, ECPrivateKey> {
    /**
     * Getter for an EC Public Key instance using the received Key Id.
     * @param keyId the Key Id specified in the JWT header ("kid" claim)
     * @return an ECPublicKey instance for verification or null if not found
     */
    ECPublicKey getPublicKeyById(String keyId);
    
    /**
     * Getter for an EC Private Key instance used for signing JWTs.
     * @return an ECPrivateKey instance for signing or null if not available
     */
    ECPrivateKey getPrivateKey();
    
    /**
     * Getter for the Id of the EC Private Key used for signing JWTs.
     * @return the key id of the private key or null if not specified
     */
    String getPrivateKeyId();
}

Usage Examples:

import com.auth0.jwt.interfaces.ECDSAKeyProvider;
import com.auth0.jwt.algorithms.Algorithm;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.ECPrivateKey;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

// ECDSA key provider with thread-safe key storage
public class ConcurrentECDSAKeyProvider implements ECDSAKeyProvider {
    private final Map<String, ECPublicKey> publicKeys;
    private final ECPrivateKey privateKey;
    private final String privateKeyId;
    
    public ConcurrentECDSAKeyProvider(ECPrivateKey privateKey, String privateKeyId) {
        this.privateKey = privateKey;
        this.privateKeyId = privateKeyId;
        this.publicKeys = new ConcurrentHashMap<>();
    }
    
    public void addPublicKey(String keyId, ECPublicKey publicKey) {
        publicKeys.put(keyId, publicKey);
    }
    
    public void removePublicKey(String keyId) {
        publicKeys.remove(keyId);
    }
    
    @Override
    public ECPublicKey getPublicKeyById(String keyId) {
        return publicKeys.get(keyId);
    }
    
    @Override
    public ECPrivateKey getPrivateKey() {
        return privateKey;
    }
    
    @Override
    public String getPrivateKeyId() {
        return privateKeyId;
    }
}

// Usage with ECDSA algorithm
ECDSAKeyProvider keyProvider = new ConcurrentECDSAKeyProvider(ecPrivateKey, "ec-key-1");
keyProvider.addPublicKey("ec-key-1", ecPublicKey1);
keyProvider.addPublicKey("ec-key-2", ecPublicKey2);

Algorithm algorithm = Algorithm.ECDSA256(keyProvider);

// Create and verify JWT
String token = JWT.create()
    .withKeyId("ec-key-1")
    .withSubject("user123")
    .sign(algorithm);

JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token);

Advanced Key Provider Patterns

Common patterns for implementing sophisticated key management scenarios.

Key Rotation Support:

import java.time.Instant;
import java.util.concurrent.ConcurrentHashMap;

public class RotatingRSAKeyProvider implements RSAKeyProvider {
    private final Map<String, KeyWithExpiry> publicKeys;
    private volatile RSAPrivateKey currentPrivateKey;
    private volatile String currentPrivateKeyId;
    
    private static class KeyWithExpiry {
        final RSAPublicKey key;
        final Instant expiresAt;
        
        KeyWithExpiry(RSAPublicKey key, Instant expiresAt) {
            this.key = key;
            this.expiresAt = expiresAt;
        }
        
        boolean isExpired() {
            return Instant.now().isAfter(expiresAt);
        }
    }
    
    public RotatingRSAKeyProvider() {
        this.publicKeys = new ConcurrentHashMap<>();
    }
    
    public void addPublicKey(String keyId, RSAPublicKey key, Instant expiresAt) {
        publicKeys.put(keyId, new KeyWithExpiry(key, expiresAt));
    }
    
    public void rotatePrivateKey(RSAPrivateKey newPrivateKey, String newKeyId) {
        this.currentPrivateKey = newPrivateKey;
        this.currentPrivateKeyId = newKeyId;
    }
    
    @Override
    public RSAPublicKey getPublicKeyById(String keyId) {
        KeyWithExpiry keyWithExpiry = publicKeys.get(keyId);
        if (keyWithExpiry == null || keyWithExpiry.isExpired()) {
            return null;
        }
        return keyWithExpiry.key;
    }
    
    @Override
    public RSAPrivateKey getPrivateKey() {
        return currentPrivateKey;
    }
    
    @Override
    public String getPrivateKeyId() {
        return currentPrivateKeyId;
    }
    
    // Cleanup expired keys
    public void cleanupExpiredKeys() {
        publicKeys.entrySet().removeIf(entry -> entry.getValue().isExpired());
    }
}

Remote Key Fetching:

import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.security.spec.X509EncodedKeySpec;
import java.security.KeyFactory;
import java.util.Base64;

public class RemoteRSAKeyProvider implements RSAKeyProvider {
    private final HttpClient httpClient;
    private final String jwksUrl;
    private final Map<String, RSAPublicKey> keyCache;
    private final RSAPrivateKey privateKey;
    private final String privateKeyId;
    
    public RemoteRSAKeyProvider(String jwksUrl, RSAPrivateKey privateKey, String privateKeyId) {
        this.httpClient = HttpClient.newHttpClient();
        this.jwksUrl = jwksUrl;
        this.keyCache = new ConcurrentHashMap<>();
        this.privateKey = privateKey;
        this.privateKeyId = privateKeyId;
    }
    
    @Override
    public RSAPublicKey getPublicKeyById(String keyId) {
        // Check cache first
        RSAPublicKey cachedKey = keyCache.get(keyId);
        if (cachedKey != null) {
            return cachedKey;
        }
        
        // Fetch from remote JWKS endpoint
        try {
            RSAPublicKey fetchedKey = fetchPublicKeyFromJwks(keyId);
            if (fetchedKey != null) {
                keyCache.put(keyId, fetchedKey);
            }
            return fetchedKey;
        } catch (Exception e) {
            // Log error and return null
            System.err.println("Failed to fetch public key " + keyId + ": " + e.getMessage());
            return null;
        }
    }
    
    private RSAPublicKey fetchPublicKeyFromJwks(String keyId) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(jwksUrl))
            .GET()
            .build();
            
        HttpResponse<String> response = httpClient.send(request, 
            HttpResponse.BodyHandlers.ofString());
            
        if (response.statusCode() != 200) {
            throw new RuntimeException("Failed to fetch JWKS: " + response.statusCode());
        }
        
        // Parse JWKS response and extract public key for keyId
        // This is a simplified example - real implementation would use JSON parsing
        return parseJwksResponse(response.body(), keyId);
    }
    
    private RSAPublicKey parseJwksResponse(String jwksJson, String keyId) throws Exception {
        // Simplified parsing - use a proper JSON library in production
        // Extract the base64-encoded public key for the given keyId
        // Convert to RSAPublicKey and return
        
        // This is pseudo-code for demonstration
        String publicKeyBase64 = extractPublicKeyFromJwks(jwksJson, keyId);
        byte[] keyBytes = Base64.getDecoder().decode(publicKeyBase64);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return (RSAPublicKey) keyFactory.generatePublic(keySpec);
    }
    
    private String extractPublicKeyFromJwks(String jwksJson, String keyId) {
        // Implement JWKS parsing logic here
        return ""; // Placeholder
    }
    
    @Override
    public RSAPrivateKey getPrivateKey() {
        return privateKey;
    }
    
    @Override
    public String getPrivateKeyId() {
        return privateKeyId;
    }
}

Multi-Tenant Key Provider:

public class MultiTenantRSAKeyProvider implements RSAKeyProvider {
    private final Map<String, TenantKeySet> tenantKeys;
    private final String currentTenant;
    
    private static class TenantKeySet {
        final Map<String, RSAPublicKey> publicKeys;
        final RSAPrivateKey privateKey;
        final String privateKeyId;
        
        TenantKeySet(Map<String, RSAPublicKey> publicKeys, 
                    RSAPrivateKey privateKey, 
                    String privateKeyId) {
            this.publicKeys = new HashMap<>(publicKeys);
            this.privateKey = privateKey;
            this.privateKeyId = privateKeyId;
        }
    }
    
    public MultiTenantRSAKeyProvider(String currentTenant) {
        this.tenantKeys = new ConcurrentHashMap<>();
        this.currentTenant = currentTenant;
    }
    
    public void addTenant(String tenantId, Map<String, RSAPublicKey> publicKeys, 
                         RSAPrivateKey privateKey, String privateKeyId) {
        tenantKeys.put(tenantId, new TenantKeySet(publicKeys, privateKey, privateKeyId));
    }
    
    @Override
    public RSAPublicKey getPublicKeyById(String keyId) {
        TenantKeySet tenantKeySet = tenantKeys.get(currentTenant);
        if (tenantKeySet == null) {
            return null;
        }
        return tenantKeySet.publicKeys.get(keyId);
    }
    
    @Override
    public RSAPrivateKey getPrivateKey() {
        TenantKeySet tenantKeySet = tenantKeys.get(currentTenant);
        return tenantKeySet != null ? tenantKeySet.privateKey : null;
    }
    
    @Override
    public String getPrivateKeyId() {
        TenantKeySet tenantKeySet = tenantKeys.get(currentTenant);
        return tenantKeySet != null ? tenantKeySet.privateKeyId : null;
    }
}

JWTPartsParser Interface

Interface for parsing JWT header and payload JSON strings into structured objects.

/**
 * Parser interface for JWT parts providing structured access to header and payload data
 */
public interface JWTPartsParser {
    /**
     * Parse a payload JSON string into a Payload object.
     * @param json the JSON string representing the payload
     * @return a Payload instance providing access to payload claims
     * @throws com.auth0.jwt.exceptions.JWTDecodeException if JSON parsing fails
     */
    Payload parsePayload(String json) throws JWTDecodeException;
    
    /**
     * Parse a header JSON string into a Header object.
     * @param json the JSON string representing the header
     * @return a Header instance providing access to header claims
     * @throws com.auth0.jwt.exceptions.JWTDecodeException if JSON parsing fails
     */
    Header parseHeader(String json) throws JWTDecodeException;
}

Usage Examples:

import com.auth0.jwt.interfaces.JWTPartsParser;
import com.auth0.jwt.interfaces.Payload;
import com.auth0.jwt.interfaces.Header;
import com.auth0.jwt.impl.JWTParser;

// Use the default parser implementation
JWTPartsParser parser = new JWTParser();

// Parse header JSON
String headerJson = "{\"alg\":\"HS256\",\"typ\":\"JWT\",\"kid\":\"key-1\"}";
try {
    Header header = parser.parseHeader(headerJson);
    String algorithm = header.getAlgorithm(); // "HS256"
    String keyId = header.getKeyId();         // "key-1"
    System.out.println("Parsed algorithm: " + algorithm);
} catch (JWTDecodeException e) {
    System.err.println("Failed to parse header: " + e.getMessage());
}

// Parse payload JSON
String payloadJson = "{\"sub\":\"user123\",\"iss\":\"auth0\",\"exp\":1234567890}";
try {
    Payload payload = parser.parsePayload(payloadJson);
    String subject = payload.getSubject(); // "user123"
    String issuer = payload.getIssuer();   // "auth0"
    System.out.println("Parsed subject: " + subject);
} catch (JWTDecodeException e) {
    System.err.println("Failed to parse payload: " + e.getMessage());
}

Best Practices

Key Security

  • Private Key Protection: Store private keys securely using hardware security modules (HSMs) or secure key management services
  • Key Rotation: Implement regular key rotation to limit exposure window
  • Access Control: Restrict access to private keys to authorized processes only

Performance Optimization

  • Key Caching: Cache frequently accessed public keys to avoid repeated network calls
  • Connection Pooling: Use connection pooling for remote key fetching
  • Async Loading: Consider asynchronous key loading for high-throughput scenarios

Error Handling

  • Graceful Degradation: Handle key fetch failures gracefully with appropriate fallbacks
  • Logging: Log key resolution failures for monitoring and debugging
  • Timeouts: Implement timeouts for remote key fetching to prevent blocking

Multi-Tenant Considerations

  • Tenant Isolation: Ensure complete isolation of keys between tenants
  • Resource Limits: Implement limits on cached keys per tenant
  • Audit Logging: Log key access for security auditing

Types

/**
 * Generic key provider interface for dynamic key resolution
 */
public interface KeyProvider<U, R> {
    U getPublicKeyById(String keyId);
    R getPrivateKey();
    String getPrivateKeyId();
}

/**
 * RSA-specific key provider interface
 */
public interface RSAKeyProvider extends KeyProvider<RSAPublicKey, RSAPrivateKey> {
    RSAPublicKey getPublicKeyById(String keyId);
    RSAPrivateKey getPrivateKey();
    String getPrivateKeyId();
}

/**
 * ECDSA-specific key provider interface
 */
public interface ECDSAKeyProvider extends KeyProvider<ECPublicKey, ECPrivateKey> {
    ECPublicKey getPublicKeyById(String keyId);
    ECPrivateKey getPrivateKey();
    String getPrivateKeyId();
}

/**
 * JWT parts parser interface
 */
public interface JWTPartsParser {
    Payload parsePayload(String json) throws JWTDecodeException;
    Header parseHeader(String json) throws JWTDecodeException;
}

Install with Tessl CLI

npx tessl i tessl/maven-com-auth0--java-jwt

docs

algorithms.md

claims.md

index.md

jwt-creation.md

jwt-decoding.md

jwt-verification.md

key-providers.md

tile.json