JWT Decoder
Header
Payload
Signature
Claims explained
| Claim | Value | Meaning |
|---|
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)
| Claim | Full name | Type | Meaning |
|---|---|---|---|
iss | Issuer | String | Who issued the token |
sub | Subject | String | Who the token is about (user ID) |
aud | Audience | String or array | Intended recipient(s) |
exp | Expiration | Unix timestamp (s) | Must not be accepted after this time |
nbf | Not before | Unix timestamp (s) | Must not be accepted before this time |
iat | Issued at | Unix timestamp (s) | When the token was created |
jti | JWT ID | String | Unique 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
issandaud. 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.
Related tools and guides
- Base64 Encoder / Decoder β the encoding behind JWT parts
- UUID Generator β for the
jticlaim - Epoch Timestamp β decode
exp/iathuman-readably - Java Online Compiler β try the jjwt snippets above
- JSON to Java POJO β generate a claims record from the payload