Skip to content
AldeaCode Logo
JWT Decoder / Java Developer 100% local

Decode JWT in Java: jjwt, java-jwt, or zero-deps Base64

A JWT in Java is the same RFC 7519 envelope as anywhere else: header, payload, signature, separated by dots, each Base64URL encoded. The decision is which library you reach for, and whether you actually need verification or just inspection.

Decoding is not verifying

Splitting a token on the dots and Base64URL-decoding the middle segment gives you the claims. That is decoding. The signature stays untouched, so any tampering goes unnoticed.

In production code, decode without verify is a vulnerability. The classic exploit changes the header alg to none, drops the signature, and any service that trusts decoded claims accepts the forgery. Keep decode for debugging, logs, and tests. Use verify for everything that grants access.

io.jsonwebtoken (jjwt) is the common pick

io.jsonwebtoken:jjwt-api plus the -impl and -jackson runtime artefacts is the most common Java JWT stack. The API splits parsing from validation, which makes the security boundary explicit:

Jws<Claims> jws = Jwts.parserBuilder()
    .setSigningKey(secretKey)
    .build()
    .parseClaimsJws(token);
Claims claims = jws.getBody();

Key import is verbose for RSA and EC keys, but the resulting code rejects alg: none by default and refuses tokens whose header algorithm does not match the key type. Both are mistakes that earlier libraries shipped.

com.auth0:java-jwt as the alternative

Auth0's java-jwt is the other widely used option. The fluent builder is a different style:

DecodedJWT jwt = JWT.require(Algorithm.HMAC256(secret))
    .build()
    .verify(token);
String userId = jwt.getClaim("sub").asString();

It also exposes JWT.decode(token) for inspection, returning a DecodedJWT without verifying the signature. Useful for logs, dangerous in production paths. Same warning as before: the moment a controller trusts those claims, you have a bug.

Zero-deps decode for debugging

If you just want to inspect a token from a curl response, java.util.Base64.getUrlDecoder() and String.split("\\.") are enough. No Maven coordinates, no transitive dependencies. The example below prints the header and payload of any compact JWT.

Working example

java
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class JwtInspector {
    public static void main(String[] args) {
        String token = "eyJhbGciOiJIUzI1NiJ9."
                     + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFkYSJ9."
                     + "5mhBHqs5_DTLdINd9p5m7ZJ6XD0Xc55kIaCRY5r6HRA";

        String[] parts = token.split("\\.");
        if (parts.length < 2) {
            throw new IllegalArgumentException("Not a JWT");
        }

        Base64.Decoder dec = Base64.getUrlDecoder();
        String header = new String(dec.decode(parts[0]), StandardCharsets.UTF_8);
        String payload = new String(dec.decode(parts[1]), StandardCharsets.UTF_8);

        System.out.println("Header:  " + header);
        System.out.println("Payload: " + payload);
        // Production code: now verify the signature with jjwt or java-jwt.
    }
}

Just need the result?

When you are mid-debug and want to see what is inside a JWT without writing a Java main class, paste it into the JWT decoder on aldeacode.com. Header and payload pop up instantly, the signature stays opaque on purpose, and nothing leaves your browser. Use it as the inspection step before you reach for verification in real code.

Open JWT Decoder →

Frequently asked questions

Why does jjwt split the API into api, impl, and jackson modules?

Separating the public API from the implementation lets the maintainers ship breaking changes inside impl without breaking compile-time consumers. The jackson artefact is the JSON binding; switch to gson if you already pull Gson elsewhere and want one fewer dependency.

Is the manual Base64 split safe for production decoding?

For decoding only, yes. For anything that grants access, no. The manual approach has no signature check, no exp validation, no audience check. Use a real library for any flow that depends on the token being trustworthy.

What is the alg=none attack and is it still a risk?

An attacker rewrites the header alg to none, drops the signature, and submits the token. Old libraries accepted it. Modern jjwt and java-jwt reject it by default. If you still maintain code that calls a parser without specifying the expected algorithm, fix that today.