Auth0 Java JWT

authenticationJWTJavaKotlinAuth0tokensecurityserver-side

Library

Auth0 Java JWT

Overview

Auth0 Java JWT is a library for creating, verifying, and decoding JSON Web Tokens (JWT) in Java applications.

Details

Auth0 Java JWT is a JSON Web Token implementation library for Java and Kotlin developed and provided by Auth0. It specializes in JWT processing for server-side JVM applications and supports Java LTS versions (8, 11, 17).

This library provides JWT generation, signing, and verification functionality compliant with JWT specifications, supporting multiple cryptographic algorithms including HMAC, RSA, and ECDSA. It supports both signing and verification, providing comprehensive functionality needed to build secure authentication systems.

Key features include intuitive token operations through fluent API design, rich claim management functionality, error handling through exceptions, and complete compatibility with the Kotlin language. Integration with the Auth0 ecosystem makes it easy to build enterprise-level authentication infrastructure.

The library is provided under the MIT license and is actively maintained. For Android, a dedicated library called JWTDecode.Android is provided separately with platform-specific optimizations.

Pros and Cons

Pros

  • Rich Algorithm Support: Comprehensive support for major signing algorithms including HMAC, RSA, and ECDSA
  • Intuitive API: Fluent and readable code through method chaining
  • Kotlin Support: Complete compatibility with both Java and Kotlin languages
  • Security-focused: Compliant with industry-standard security practices
  • Auth0 Integration: Seamless integration with the Auth0 platform
  • Active Maintenance: Ongoing development and support
  • Rich Documentation: Comprehensive official documentation and sample code

Cons

  • JVM Limited: Only works in JVM-based environments
  • No Android Support: Requires separate library for Android
  • Auth0 Dependency: Design specialized for Auth0 ecosystem
  • Learning Cost: Requires understanding of JWT specifications and security
  • Debug Complexity: Sometimes difficult to identify causes of token-related errors
  • Configuration Complexity: Complexity when setting up advanced security configurations

Key Links

Usage Examples

Basic Setup

<!-- Maven dependency -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.5.0</version>
</dependency>
// Gradle dependency
implementation 'com.auth0:java-jwt:4.5.0'

JWT Token Creation (HMAC)

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;

public class JWTService {
    private static final String SECRET = "your-256-bit-secret";
    
    public String createToken(String userId, String email) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            String token = JWT.create()
                .withIssuer("my-app")
                .withSubject(userId)
                .withClaim("email", email)
                .withClaim("role", "user")
                .withIssuedAt(new Date())
                .withExpiresAt(new Date(System.currentTimeMillis() + 3600000)) // 1 hour
                .sign(algorithm);
            return token;
        } catch (JWTCreationException exception) {
            throw new RuntimeException("JWT creation error", exception);
        }
    }
}

JWT Token Verification (HMAC)

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;

public class JWTVerifier {
    private static final String SECRET = "your-256-bit-secret";
    
    public DecodedJWT verifyToken(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer("my-app")
                .build();
            return verifier.verify(token);
        } catch (JWTVerificationException exception) {
            throw new RuntimeException("JWT verification error", exception);
        }
    }
    
    public boolean isTokenValid(String token) {
        try {
            verifyToken(token);
            return true;
        } catch (RuntimeException e) {
            return false;
        }
    }
    
    public String getUserIdFromToken(String token) {
        DecodedJWT jwt = verifyToken(token);
        return jwt.getSubject();
    }
    
    public String getEmailFromToken(String token) {
        DecodedJWT jwt = verifyToken(token);
        return jwt.getClaim("email").asString();
    }
}

JWT with RSA Encryption

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class RSAJWTService {
    private final RSAPrivateKey privateKey;
    private final RSAPublicKey publicKey;
    
    public RSAJWTService(String privateKeyPath, String publicKeyPath) 
            throws Exception {
        this.privateKey = loadPrivateKey(privateKeyPath);
        this.publicKey = loadPublicKey(publicKeyPath);
    }
    
    public String createRSAToken(String userId, String email) {
        try {
            Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);
            return JWT.create()
                .withIssuer("secure-app")
                .withSubject(userId)
                .withClaim("email", email)
                .withClaim("scope", "read write")
                .withIssuedAt(new Date())
                .withExpiresAt(new Date(System.currentTimeMillis() + 7200000)) // 2 hours
                .sign(algorithm);
        } catch (JWTCreationException exception) {
            throw new RuntimeException("RSA JWT creation error", exception);
        }
    }
    
    public DecodedJWT verifyRSAToken(String token) {
        try {
            Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);
            JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer("secure-app")
                .build();
            return verifier.verify(token);
        } catch (JWTVerificationException exception) {
            throw new RuntimeException("RSA JWT verification error", exception);
        }
    }
    
    private RSAPrivateKey loadPrivateKey(String keyPath) throws Exception {
        String keyContent = new String(Files.readAllBytes(Paths.get(keyPath)))
            .replace("-----BEGIN PRIVATE KEY-----", "")
            .replace("-----END PRIVATE KEY-----", "")
            .replaceAll("\\s", "");
        
        byte[] keyBytes = Base64.getDecoder().decode(keyContent);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return (RSAPrivateKey) factory.generatePrivate(spec);
    }
    
    private RSAPublicKey loadPublicKey(String keyPath) throws Exception {
        String keyContent = new String(Files.readAllBytes(Paths.get(keyPath)))
            .replace("-----BEGIN PUBLIC KEY-----", "")
            .replace("-----END PUBLIC KEY-----", "")
            .replaceAll("\\s", "");
        
        byte[] keyBytes = Base64.getDecoder().decode(keyContent);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return (RSAPublicKey) factory.generatePublic(spec);
    }
}

Spring Boot Integration

import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Value;

@Service
public class AuthenticationService {
    
    @Value("${jwt.secret}")
    private String jwtSecret;
    
    @Value("${jwt.expiration}")
    private long jwtExpiration;
    
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("authorities", userDetails.getAuthorities()
            .stream()
            .map(GrantedAuthority::getAuthority)
            .collect(Collectors.toList()));
        
        return createToken(claims, userDetails.getUsername());
    }
    
    private String createToken(Map<String, Object> claims, String subject) {
        Algorithm algorithm = Algorithm.HMAC256(jwtSecret);
        
        return JWT.create()
            .withSubject(subject)
            .withIssuedAt(new Date())
            .withExpiresAt(new Date(System.currentTimeMillis() + jwtExpiration))
            .withClaim("authorities", (List<String>) claims.get("authorities"))
            .sign(algorithm);
    }
    
    public String extractUsername(String token) {
        return verifyToken(token).getSubject();
    }
    
    public List<String> extractAuthorities(String token) {
        return verifyToken(token).getClaim("authorities").asList(String.class);
    }
    
    public boolean isTokenExpired(String token) {
        return verifyToken(token).getExpiresAt().before(new Date());
    }
    
    public boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
    
    private DecodedJWT verifyToken(String token) {
        Algorithm algorithm = Algorithm.HMAC256(jwtSecret);
        JWTVerifier verifier = JWT.require(algorithm).build();
        return verifier.verify(token);
    }
}

Kotlin Implementation Example

import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import com.auth0.jwt.exceptions.JWTCreationException
import com.auth0.jwt.exceptions.JWTVerificationException
import com.auth0.jwt.interfaces.DecodedJWT
import java.util.*

class KotlinJWTService(private val secret: String) {
    
    fun createToken(
        userId: String,
        email: String,
        roles: List<String> = emptyList()
    ): String {
        return try {
            val algorithm = Algorithm.HMAC256(secret)
            JWT.create()
                .withIssuer("kotlin-app")
                .withSubject(userId)
                .withClaim("email", email)
                .withClaim("roles", roles)
                .withIssuedAt(Date())
                .withExpiresAt(Date(System.currentTimeMillis() + 3600000L))
                .sign(algorithm)
        } catch (exception: JWTCreationException) {
            throw RuntimeException("JWT creation error", exception)
        }
    }
    
    fun verifyToken(token: String): DecodedJWT {
        return try {
            val algorithm = Algorithm.HMAC256(secret)
            val verifier = JWT.require(algorithm)
                .withIssuer("kotlin-app")
                .build()
            verifier.verify(token)
        } catch (exception: JWTVerificationException) {
            throw RuntimeException("JWT verification error", exception)
        }
    }
    
    fun extractClaims(token: String): TokenClaims {
        val jwt = verifyToken(token)
        return TokenClaims(
            userId = jwt.subject,
            email = jwt.getClaim("email").asString(),
            roles = jwt.getClaim("roles").asList(String::class.java),
            issuedAt = jwt.issuedAt,
            expiresAt = jwt.expiresAt
        )
    }
    
    fun isTokenValid(token: String): Boolean {
        return try {
            verifyToken(token)
            true
        } catch (e: RuntimeException) {
            false
        }
    }
}

data class TokenClaims(
    val userId: String,
    val email: String,
    val roles: List<String>,
    val issuedAt: Date,
    val expiresAt: Date
)

Refresh Token Implementation

@Component
public class RefreshTokenService {
    
    private final String accessSecret;
    private final String refreshSecret;
    private final long accessTokenExpiration;
    private final long refreshTokenExpiration;
    
    public RefreshTokenService(
            @Value("${jwt.access.secret}") String accessSecret,
            @Value("${jwt.refresh.secret}") String refreshSecret,
            @Value("${jwt.access.expiration}") long accessTokenExpiration,
            @Value("${jwt.refresh.expiration}") long refreshTokenExpiration) {
        this.accessSecret = accessSecret;
        this.refreshSecret = refreshSecret;
        this.accessTokenExpiration = accessTokenExpiration;
        this.refreshTokenExpiration = refreshTokenExpiration;
    }
    
    public TokenPair generateTokenPair(String userId, String email) {
        String accessToken = createAccessToken(userId, email);
        String refreshToken = createRefreshToken(userId);
        
        return new TokenPair(accessToken, refreshToken);
    }
    
    private String createAccessToken(String userId, String email) {
        Algorithm algorithm = Algorithm.HMAC256(accessSecret);
        return JWT.create()
            .withIssuer("app")
            .withSubject(userId)
            .withClaim("email", email)
            .withClaim("type", "access")
            .withIssuedAt(new Date())
            .withExpiresAt(new Date(System.currentTimeMillis() + accessTokenExpiration))
            .sign(algorithm);
    }
    
    private String createRefreshToken(String userId) {
        Algorithm algorithm = Algorithm.HMAC256(refreshSecret);
        return JWT.create()
            .withIssuer("app")
            .withSubject(userId)
            .withClaim("type", "refresh")
            .withIssuedAt(new Date())
            .withExpiresAt(new Date(System.currentTimeMillis() + refreshTokenExpiration))
            .sign(algorithm);
    }
    
    public TokenPair refreshAccessToken(String refreshToken) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(refreshSecret);
            JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer("app")
                .withClaim("type", "refresh")
                .build();
            
            DecodedJWT jwt = verifier.verify(refreshToken);
            String userId = jwt.getSubject();
            
            // Get user information (from database)
            String email = getUserEmail(userId);
            
            return generateTokenPair(userId, email);
        } catch (JWTVerificationException e) {
            throw new RuntimeException("Invalid refresh token", e);
        }
    }
    
    private String getUserEmail(String userId) {
        // Get user information from database
        return "[email protected]"; // Implementation example
    }
    
    public static class TokenPair {
        private final String accessToken;
        private final String refreshToken;
        
        public TokenPair(String accessToken, String refreshToken) {
            this.accessToken = accessToken;
            this.refreshToken = refreshToken;
        }
        
        // getters
        public String getAccessToken() { return accessToken; }
        public String getRefreshToken() { return refreshToken; }
    }
}

JWT Decoding (Without Verification)

public class JWTDecoder {
    
    public void decodeTokenWithoutVerification(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            
            System.out.println("Header:");
            System.out.println("  Algorithm: " + jwt.getAlgorithm());
            System.out.println("  Type: " + jwt.getType());
            
            System.out.println("Payload:");
            System.out.println("  Issuer: " + jwt.getIssuer());
            System.out.println("  Subject: " + jwt.getSubject());
            System.out.println("  Audience: " + jwt.getAudience());
            System.out.println("  Expires At: " + jwt.getExpiresAt());
            System.out.println("  Not Before: " + jwt.getNotBefore());
            System.out.println("  Issued At: " + jwt.getIssuedAt());
            System.out.println("  JWT ID: " + jwt.getId());
            
            System.out.println("Custom Claims:");
            jwt.getClaims().forEach((key, claim) -> {
                if (!isStandardClaim(key)) {
                    System.out.println("  " + key + ": " + claim.asString());
                }
            });
            
        } catch (JWTDecodeException exception) {
            System.err.println("Invalid JWT token: " + exception.getMessage());
        }
    }
    
    private boolean isStandardClaim(String claimName) {
        return Arrays.asList("iss", "sub", "aud", "exp", "nbf", "iat", "jti")
            .contains(claimName);
    }
}