jwtk/jjwt · error · UnsupportedJwtException

The JWT header references compression algorithm '%s', but pa

Error message

The JWT header references compression algorithm '%s', but payload decompression for Unencoded JWSs (those with a b64 header value of false) that rely on a SigningKeyResolver are disallowed by default to protect against [Denial of Service attacks](https://www.usenix.org/system/files/conference/usenixsecurity15/sec15-paper-pellegrino.pdf).  If you wish to enable Unencoded JWS payload decompression, configure the JwtParserBuilder.keyLocator(Locator) and do not configure a SigningKeyResolver.

What it means

For unencoded JWSs (b64=false) whose payload compression is requested, the library refuses to decompress when integrity wasn't cryptographically verified and a SigningKeyResolver is in use, because decompressing attacker-controlled data enables DoS (zip bombs). It throws UnsupportedJwtException instructing you to switch to a KeyLocator.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java:586

            encAlg.decrypt(dreq, plaintext);
            payload = new Payload(plaintext.toByteArray(), header.getContentType());

            integrityVerified = true; // AEAD performs integrity verification, so no exception = verified

        } else if (hasDigest && this.signingKeyResolver == null) { //TODO: for 1.0, remove the == null check
            // not using a signing key resolver, so we can verify the signature before reading the payload, which is
            // always safer:
            JwsHeader jwsHeader = Assert.stateIsInstance(JwsHeader.class, header, "Not a JwsHeader. ");
            digest = verifySignature(tokenized, jwsHeader, alg, new LocatingKeyResolver(this.keyLocator), null, payload);
            integrityVerified = true; // no exception means signature verified
        }

        final CompressionAlgorithm compressionAlgorithm = zipAlgs.apply(header);
        if (compressionAlgorithm != null) {
            if (!integrityVerified) {
                if (!payloadBase64UrlEncoded) {
                    String msg = String.format(B64_DECOMPRESSION_MSG, compressionAlgorithm.getId());
                    throw new UnsupportedJwtException(msg);
                } else if (!unsecuredDecompression) {
                    String msg = String.format(UNPROTECTED_DECOMPRESSION_MSG, compressionAlgorithm.getId());
                    throw new UnsupportedJwtException(msg);
                }
            }
            payload = payload.decompress(compressionAlgorithm);
        }

        Claims claims = null;
        byte[] payloadBytes = payload.getBytes();
        if (payload.isConsumable()) {
            InputStream in = null;
            try {
                in = payload.toInputStream();

                if (!hasContentType(header)) {   // If there is a content type set, then the application using JJWT is expected
                    //                          to convert the byte payload themselves based on this content type
                    //                          https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10 :

View on GitHub (pinned to fb71496164)

Solutions

  1. Replace SigningKeyResolver with JwtParserBuilder.keyLocator(Locator) so integrity is verified before decompression.
  2. Remove compression (zip header) from unencoded JWSs, or use standard encoded JWSs where decompression after verification is safe.
  3. If you must keep the old flow and accept the risk, call .unsecuredDecompression() on the builder after reading its security JavaDoc.
  4. Verify the token producer really needs b64:false; most use cases should not use unencoded payloads.

Example fix

// before
Jwts.parser().setSigningKeyResolver(resolver).build().parse(jws);
// after
Jwts.parser().keyLocator(header -> key).build().parse(jws);
Defensive patterns

Strategy: try-catch

Validate before calling

Map<String,Object> h = getUntrustedHeader(token); // decode header without parsing payload
boolean unsafe = h.containsKey("zip") && h.containsKey("b64") && Boolean.FALSE.equals(h.get("b64")) && usingSigningKeyResolver;

Try / catch

try { parser.parse(jws); } catch (UnsupportedJwtException e) { if (e.getMessage().contains("decompression")) { /* migrate to keyLocator or enable explicitly */ } }

Prevention

When it happens

Trigger: Parsing an unencoded JWS (b64:false header) with a zip/def header while a SigningKeyResolver is configured and unsecuredDecompression() was not called.

Common situations: Legacy code using deprecated setSigningKeyResolver APIs combined with compressed unencoded payloads, migration from older JJWT versions where this was allowed, tokens produced with custom unencoded+compressed formats.

Related errors


AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09). Data as JSON: /api/errors/f4eb9d7807dfd0cc. Report an issue: GitHub.