JWT Decoder

Header

Payload

Signature

Claims explained

ClaimValueMeaning

What is a JWT?

A JSON Web Token (JWT, pronounced "jot") is an industry-standard (RFC 7519) way to transmit signed claims between two parties. You'll find JWTs behind most modern authentication schemes: OAuth 2.0 bearer tokens, OpenID Connect ID tokens, API keys, email verification links.

A JWT is a string made of three parts separated by dots:

xxxxx.yyyyy.zzzzz
header . payload . signature

Each section is Base64 URL-safe encoded. The header and payload are JSON documents. The signature proves the first two parts haven't been tampered with.

Anatomy of a JWT

Header

Metadata about the token, typically:

{
  "alg": "HS256",
  "typ": "JWT",
  "kid": "key-2024"
}
  • alg β€” signing algorithm (HS256, RS256, ES256…)
  • typ β€” almost always "JWT"
  • kid β€” key identifier (hint for servers rotating keys)

Payload

Contains the claims β€” statements about the user or the request. Standard claims (defined in RFC 7519) use short names:

{
  "iss": "auth.example.com",
  "sub": "user-42",
  "aud": "my-api",
  "iat": 1713700000,
  "exp": 1713703600,
  "roles": ["admin", "user"]
}

You can add any custom claim alongside the standard ones. Keep the payload small β€” it travels with every request.

Signature

Created by signing base64url(header) + "." + base64url(payload) with the secret (HS) or private key (RS / ES). The recipient re-computes the signature with the same secret or public key and compares.

Standard claims (RFC 7519)

ClaimFull nameTypeMeaning
issIssuerStringWho issued the token
subSubjectStringWho the token is about (user ID)
audAudienceString or arrayIntended recipient(s)
expExpirationUnix timestamp (s)Must not be accepted after this time
nbfNot beforeUnix timestamp (s)Must not be accepted before this time
iatIssued atUnix timestamp (s)When the token was created
jtiJWT IDStringUnique ID β€” useful for revocation lists

All timestamp claims are in seconds since epoch, not milliseconds.

Working with JWTs in Java

jjwt (JSON Web Token for Java)

The most popular library. Clean builder API, good defaults.

<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-api</artifactId>
  <version>0.12.6</version>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-impl</artifactId>
  <version>0.12.6</version>
  <scope>runtime</scope>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-jackson</artifactId>
  <version>0.12.6</version>
  <scope>runtime</scope>
</dependency>

Creating a signed JWT

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.time.Instant;
import java.time.temporal.ChronoUnit;

SecretKey key = Keys.hmacShaKeyFor("a-very-long-and-secret-key-32-chars+".getBytes());

String jwt = Jwts.builder()
    .issuer("auth.example.com")
    .subject("user-42")
    .audience().add("my-api").and()
    .issuedAt(java.util.Date.from(Instant.now()))
    .expiration(java.util.Date.from(Instant.now().plus(1, ChronoUnit.HOURS)))
    .claim("roles", java.util.List.of("admin", "user"))
    .signWith(key)
    .compact();

Verifying and reading a JWT

Claims claims = Jwts.parser()
    .verifyWith(key)        // throws if signature is wrong
    .build()
    .parseSignedClaims(jwt)
    .getPayload();

String subject = claims.getSubject();
List<String> roles = claims.get("roles", List.class);

Nimbus JOSE+JWT β€” alternative

More complete but lower-level. Supports every signing/encryption algorithm and the full JOSE spec.

import com.nimbusds.jwt.SignedJWT;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.MACVerifier;

SignedJWT signed = SignedJWT.parse(token);
JWSVerifier verifier = new MACVerifier(secretBytes);
if (!signed.verify(verifier)) throw new IllegalStateException("Bad signature");
JWTClaimsSet claims = signed.getJWTClaimsSet();

Security pitfalls

  • Never accept alg: none. Some libraries historically accepted an empty signature. Make sure yours rejects it explicitly.
  • Don't store secrets in a JWT. The payload is readable by anyone who has the token β€” it's signed, not encrypted.
  • Short expirations + refresh tokens. A leaked 1-hour JWT is worse than a leaked 5-minute one. Rotate, don't just trust exp.
  • Verify the algorithm. RS256 tokens should not be accepted as HS256 (confused-deputy attack).
  • Check iss and aud. Don't assume a valid signature means the token was meant for you.
  • Use HTTPS. Always. Bearer tokens over plain HTTP are instantly stolen.

Common mistakes

  • Pasting Bearer ey... including the prefix. The decoder strips it automatically, but your server's parser may not.
  • Confusing seconds and milliseconds in exp. JWT timestamps are in seconds.
  • Assuming the decoder verified the signature. It did not.
  • Building your own JWT library instead of using jjwt / Nimbus. Timing-safe comparisons, key handling and algorithm checks are easy to get wrong.
  • Reusing a JWT secret across services. One compromised service compromises all of them.