JJWT API
Documentation
The most popular Java / Android library for creating and verifying JSON Web Tokens (JWT), JSON Web Signatures (JWS), and JSON Web Encryption (JWE). Built on top of RFC 7519.
Installation #installation
Add the following dependencies to your build file. Include only the implementation you need.
Maven
XML<!-- Core API -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.6</version>
</dependency>
<!-- Runtime: choose jackson, gson, or orgjson -->
<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>
Gradle
Groovyimplementation 'io.jsonwebtoken:jjwt-api:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
Quick Start #quickstart
Create and verify a signed JWT in under 10 lines of code.
Create a Token
Javaimport io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
// Generate a secure key (store it safely!)
SecretKey key = Keys.hmacShaKeyFor(secretBytes); // or Keys.secretKeyFor(SignatureAlgorithm.HS256)
String jwt = Jwts.builder()
.subject("user123")
.issuer("myapp")
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + 86_400_000L)) // 1 day
.claim("role", "admin")
.signWith(key)
.compact();
Parse & Verify a Token
JavaClaims claims = Jwts.parser()
.verifyWith(key)
.build()
.parseSignedClaims(jwt)
.getPayload();
String subject = claims.getSubject(); // "user123"
String role = claims.get("role", String.class); // "admin"
Jwts Factory #jwts
The Jwts class is the main entry point. All operations start here.
| Method | Return Type | Description |
|---|---|---|
| Jwts.builder() | JwtBuilder | Creates a new JWT/JWS/JWE builder. |
| Jwts.parser() | JwtParserBuilder | Creates a new JWT parser builder. |
| Jwts.claims() | ClaimsBuilder | Creates a new Claims builder (payload). |
| Jwts.header() | DynamicHeaderBuilder | Creates a new JWT header builder. |
| Jwts.SIG | StandardSignatureAlgorithms | Registry of all JWS signature algorithms. |
| Jwts.KEY | StandardKeyAlgorithms | Registry of JWE key management algorithms. |
| Jwts.ENC | StandardEncryptionAlgorithms | Registry of JWE content encryption algorithms. |
| Jwts.ZIP | StandardCompressionAlgorithms | Registry of compression codecs. |
JWT Builder #builder
Fluent API for constructing a token. Call compact() at the end to get the serialized string.
Returns a JwtBuilder after setting the JOSE header. Use the consumer overload for complex headers.
JavaJwts.builder()
.header().add("kid", "my-key-id").and()
...
| Method | Claim | Type |
|---|---|---|
| .issuer(s) | iss | String |
| .subject(s) | sub | String |
| .audience() | aud | String / Set<String> |
| .expiration(date) | exp | Date (NumericDate) |
| .notBefore(date) | nbf | Date (NumericDate) |
| .issuedAt(date) | iat | Date (NumericDate) |
| .id(s) | jti | String |
| .claim(name, val) | custom | Object |
| .claims(map) | multiple custom | Map<String,?> |
JJWT automatically selects the strongest algorithm appropriate for the key type. You may also specify the algorithm explicitly.
Java// Auto algorithm selection (recommended)
.signWith(secretKey)
// Explicit algorithm
.signWith(secretKey, Jwts.SIG.HS512)
// RSA / EC private key
.signWith(privateKey, Jwts.SIG.RS256)
JavaSecretKey aesKey = Jwts.KEY.A256GCMKW.key().build();
String jwe = Jwts.builder()
.subject("alice")
.encryptWith(aesKey, Jwts.KEY.A256GCMKW, Jwts.ENC.A256GCM)
.compact();
Claims Interface #claims
The Claims interface extends Map<String, Object> and provides typed accessors for all registered claims.
| Method | Claim | Returns |
|---|---|---|
| getIssuer() | iss | String |
| getSubject() | sub | String |
| getAudience() | aud | Set<String> |
| getExpiration() | exp | Date |
| getNotBefore() | nbf | Date |
| getIssuedAt() | iat | Date |
| getId() | jti | String |
| get(name, Class<T>) | custom | T |
JWT Parser #parser
All parsing starts with Jwts.parser(). The builder configures verification and then creates an immutable, thread-safe parser.
JwtParserBuilder Methods
| Method | Description |
|---|---|
| .verifyWith(key) | Set a secret or public key for signature verification. |
| .verifyWith(keyLocator) | Provide a Locator<SecretKey> / Locator<PublicKey> for key-per-token lookup. |
| .decryptWith(key) | Set decryption key for JWE tokens. |
| .decryptWith(keyLocator) | Dynamic key locator for JWE. |
| .requireIssuer(s) | Require iss claim to equal value. |
| .requireSubject(s) | Require sub claim. |
| .requireAudience(s) | Require aud to contain value. |
| .requireExpiration(date) | Require exp to equal value. |
| .requireIssuedAt(date) | Require iat. |
| .require(claim, value) | Require any custom claim to equal value. |
| .clockSkewSeconds(n) | Allow up to n seconds of clock skew. |
| .clock(clock) | Provide a custom Clock for time-based checks. |
| .build() | Returns an immutable JwtParser. |
JwtParser Parse Methods
| Method | Returns | Use When |
|---|---|---|
| parseSignedClaims(jwt) | Jws<Claims> | Standard signed JWT with claims payload |
| parseSignedContent(jwt) | Jws<byte[]> | Signed JWT with arbitrary byte content |
| parseEncryptedClaims(jwt) | Jwe<Claims> | Encrypted JWT with claims payload |
| parseEncryptedContent(jwt) | Jwe<byte[]> | Encrypted JWT with arbitrary content |
| parseUnsecuredClaims(jwt) | Jwt<Header,Claims> | Unsecured JWT (alg=none) — use with caution |
| parse(jwt) | Jwt<?,?> | Unknown type — returns most specific interface |
Java// Thread-safe parser — create once, reuse everywhere
JwtParser parser = Jwts.parser()
.verifyWith(publicKey)
.requireIssuer("auth-service")
.clockSkewSeconds(30)
.build();
// Parse
Jws<Claims> jws = parser.parseSignedClaims(token);
Claims payload = jws.getPayload();
JwsHeader header = jws.getHeader();
Keys & Key Generation #keys
Use the Keys utility class to safely generate or construct cryptographic keys.
Javaimport io.jsonwebtoken.security.Keys;
// ── HMAC Secret Keys ───────────────────────────────────────────
SecretKey hs256 = Jwts.SIG.HS256.key().build();
SecretKey hs384 = Jwts.SIG.HS384.key().build();
SecretKey hs512 = Jwts.SIG.HS512.key().build();
// From existing bytes (must be long enough for chosen alg)
SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(base64Secret));
// ── RSA Key Pair ───────────────────────────────────────────────
KeyPair rsaPair = Jwts.SIG.RS256.keyPair().build(); // 2048-bit
KeyPair ps512 = Jwts.SIG.PS512.keyPair().build(); // 4096-bit
// ── EC Key Pair ────────────────────────────────────────────────
KeyPair ec256 = Jwts.SIG.ES256.keyPair().build(); // P-256
KeyPair ec384 = Jwts.SIG.ES384.keyPair().build(); // P-384
KeyPair ec512 = Jwts.SIG.ES512.keyPair().build(); // P-521
// ── EdDSA (Ed25519) ────────────────────────────────────────────
KeyPair ed25519 = Jwts.SIG.Ed25519.keyPair().build();
HMAC Signature Algorithms #hmac
Available via Jwts.SIG.*. All require a SecretKey.
| Identifier | Algorithm | Min Key Length | Use Case |
|---|---|---|---|
| HS256 | HMAC-SHA-256 | 256 bits (32 bytes) | Default, widely supported |
| HS384 | HMAC-SHA-384 | 384 bits (48 bytes) | Moderate security |
| HS512 | HMAC-SHA-512 | 512 bits (64 bytes) | High security |
RSA Signature Algorithms #rsa
Sign with a PrivateKey, verify with the corresponding PublicKey.
| Identifier | Algorithm | Min Key Size |
|---|---|---|
| RS256 | RSASSA-PKCS1-v1_5 + SHA-256 | 2048 bits |
| RS384 | RSASSA-PKCS1-v1_5 + SHA-384 | 2048 bits |
| RS512 | RSASSA-PKCS1-v1_5 + SHA-512 | 4096 bits |
| PS256 | RSASSA-PSS + SHA-256 | 2048 bits |
| PS384 | RSASSA-PSS + SHA-384 | 2048 bits |
| PS512 | RSASSA-PSS + SHA-512 | 4096 bits |
Elliptic Curve Algorithms #ec
| Identifier | Curve | Signature Size |
|---|---|---|
| ES256 | P-256 + SHA-256 | 64 bytes |
| ES384 | P-384 + SHA-384 | 96 bytes |
| ES512 | P-521 + SHA-512 | 132 bytes |
EdDSA (OKP) Algorithms #eddsa
Requires JDK 15+ or Bouncy Castle on older JDKs.
| Identifier | Curve | Notes |
|---|---|---|
| Ed25519 | Edwards 25519 | Fast, secure, 64-byte signature |
| Ed448 | Edwards 448 | Higher security, 114-byte signature |
JSON Web Encryption (JWE) #jwe
JJWT supports full JWE token creation and parsing. JWE requires two algorithm choices:
Key Management Algorithm
How the Content Encryption Key (CEK) is protected. E.g. A256GCMKW, RSA-OAEP.
Content Encryption Algorithm
How the JWT payload is encrypted. E.g. A256GCM, A128CBC-HS256.
Java// ── Encrypt ────────────────────────────────────────────────────
SecretKey encKey = Jwts.ENC.A256GCM.key().build();
String jwe = Jwts.builder()
.subject("bob")
.encryptWith(encKey, Jwts.KEY.A256GCMKW, Jwts.ENC.A256GCM)
.compact();
// ── Decrypt ────────────────────────────────────────────────────
Claims claims = Jwts.parser()
.decryptWith(encKey)
.build()
.parseEncryptedClaims(jwe)
.getPayload();
JWE Key Algorithms #jwe-algorithms
| Jwts.KEY.* | RFC Name | Key Type |
|---|---|---|
| RSA1_5 | RSAES-PKCS1-v1_5 | RSA |
| RSA_OAEP | RSAES-OAEP + SHA-1 | RSA |
| RSA_OAEP_256 | RSAES-OAEP + SHA-256 | RSA |
| A128KW | AES Key Wrap 128 | AES SecretKey |
| A192KW | AES Key Wrap 192 | AES SecretKey |
| A256KW | AES Key Wrap 256 | AES SecretKey |
| A128GCMKW | AES-GCM Key Wrap 128 | AES SecretKey |
| A192GCMKW | AES-GCM Key Wrap 192 | AES SecretKey |
| A256GCMKW | AES-GCM Key Wrap 256 | AES SecretKey |
| ECDH_ES | ECDH-ES | EC / OKP |
| PBES2_HS256_A128KW | PBES2 + AES-128 | Password |
| PBES2_HS384_A192KW | PBES2 + AES-192 | Password |
| PBES2_HS512_A256KW | PBES2 + AES-256 | Password |
| DIRECT | Direct Key Agreement | AES SecretKey |
JWE Content Encryption #jwe-enc
| Jwts.ENC.* | RFC Name | CEK Size |
|---|---|---|
| A128CBC_HS256 | AES-128-CBC + HMAC-SHA-256 | 256 bits |
| A192CBC_HS384 | AES-192-CBC + HMAC-SHA-384 | 384 bits |
| A256CBC_HS512 | AES-256-CBC + HMAC-SHA-512 | 512 bits |
| A128GCM | AES-128-GCM | 128 bits |
| A192GCM | AES-192-GCM | 192 bits |
| A256GCM | AES-256-GCM | 256 bits |
Exceptions #exceptions
All JJWT exceptions extend JwtException. Handle them to give meaningful error responses.
| Exception Class | When Thrown |
|---|---|
| JwtException | Base class for all JJWT exceptions. |
| MalformedJwtException | The string is not a valid JWT structure. |
| ExpiredJwtException | The exp claim is in the past. |
| PrematureJwtException | The nbf claim is in the future. |
| MissingClaimException | A required claim is absent. |
| IncorrectClaimException | A required claim has the wrong value. |
| SignatureException | Signature verification failed. |
| SecurityException | Key is too weak for the chosen algorithm. |
| UnsupportedJwtException | Token type/alg not supported by this parser. |
Javatry {
Claims claims = parser.parseSignedClaims(token).getPayload();
} catch (ExpiredJwtException e) {
// 401 - token expired
} catch (SignatureException e) {
// 401 - tampered token
} catch (MalformedJwtException e) {
// 400 - bad token format
} catch (JwtException e) {
// 401 - catch-all
}
Compression #compression
Reduce token size when claims are large. Available via Jwts.ZIP.
| Jwts.ZIP.* | Algorithm |
|---|---|
| DEF | DEFLATE (RFC 1951) — widely supported |
| GZIP | GZIP — better ratio, less support |
JavaString compressedJwt = Jwts.builder()
.claims(largeClaims)
.compressWith(Jwts.ZIP.DEF)
.signWith(key)
.compact();
// Decompression is automatic when parsing
Claims claims = Jwts.parser()
.verifyWith(key)
.build()
.parseSignedClaims(compressedJwt)
.getPayload();
Migration Guide (0.11 → 0.12) #migration
Version 0.12 introduces a cleaner, more type-safe API. Key breaking changes:
Replace deprecated claim setters
setSubject() → subject(), setExpiration() → expiration(), setIssuedAt() → issuedAt(), etc. All setters are now builder methods without the "set" prefix.
Replace SignatureAlgorithm enum
SignatureAlgorithm.HS256 → Jwts.SIG.HS256. The old enum is removed.
Replace deprecated key helpers
Keys.secretKeyFor(SignatureAlgorithm.HS256) → Jwts.SIG.HS256.key().build()
Update parser call
Jwts.parserBuilder() → Jwts.parser(), .setSigningKey() → .verifyWith()
Update parse method names
.parseClaimsJws() → .parseSignedClaims(), .parseClaimsJwt() → .parseUnsecuredClaims()
github.com/jwtk/jjwt/blob/master/CHANGELOG.md for the complete list of changes between versions.