jwtk/jjwt · error · MalformedJwtException

JWS header does not contain a required 'alg' (Algorithm) hea

Error message

JWS header does not contain a required 'alg' (Algorithm) header parameter.  This header parameter is mandatory per the JWS Specification, Section 4.1.1. See https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.1 for more information.

What it means

jjwt rejects a compact JWT whose protected header is missing the mandatory 'alg' (Algorithm) header parameter required by RFC 7515 Section 4.1.1. During parse, the header's algorithm value is cleaned and checked; if empty, the token is malformed and cannot be processed, so a MalformedJwtException is thrown (with a JWE-specific message if the token is a JWE).

Source

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

        Map<String, ?> m = deserialize(Streams.of(headerBytes), "protected header");
        Header header;
        try {
            header = tokenized.createHeader(m);
        } catch (Exception e) {
            String msg = "Invalid protected header: " + e.getMessage();
            throw new MalformedJwtException(msg, e);
        }

        // https://tools.ietf.org/html/rfc7515#section-10.7 , second-to-last bullet point, note the use of 'always':
        //
        //   *  Require that the "alg" Header Parameter be carried in the JWS
        //      Protected Header.  (This is always the case when using the JWS
        //      Compact Serialization and is the approach taken by CMS [RFC6211].)
        //
        final String alg = Strings.clean(header.getAlgorithm());
        if (!Strings.hasText(alg)) {
            String msg = tokenized instanceof TokenizedJwe ? MISSING_JWE_ALG_MSG : MISSING_JWS_ALG_MSG;
            throw new MalformedJwtException(msg);
        }
        final boolean unsecured = Jwts.SIG.NONE.getId().equalsIgnoreCase(alg);

        final CharSequence base64UrlDigest = tokenized.getDigest();
        final boolean hasDigest = Strings.hasText(base64UrlDigest);
        if (unsecured) {
            if (tokenized instanceof TokenizedJwe) {
                throw new MalformedJwtException(JWE_NONE_MSG);
            }
            // Unsecured JWTs are disabled by default per the RFC:
            if (!this.unsecured) {
                String msg = UNSECURED_DISABLED_MSG_PREFIX + header;
                throw new UnsupportedJwtException(msg);
            }
            if (hasDigest) {
                throw new MalformedJwtException(JWS_NONE_SIG_MISMATCH_MSG);
            }
            if (header.containsKey(DefaultProtectedHeader.CRIT.getId())) {

View on GitHub (pinned to fb71496164)

Solutions

  1. Regenerate the token with a compliant JWT library so the header includes a valid 'alg' value (e.g. via JwtBuilder.header().algorithm(...) or default builder behavior).
  2. Inspect the token's first base64url segment (decode it) to confirm whether 'alg' is present and non-empty before parsing.
  3. Fix the upstream issuer/serializer to always emit 'alg'; do not work around it by pre-editing tokens.
  4. If parsing third-party tokens, validate/verify the issuer's tokens are RFC 7515 compliant.

Example fix

// before (hand-built header without alg)
String token = base64Url("{"typ":"JWT"}") + "." + payload + "." + sig;
Jws<Claims> jws = Jwts.parser().verifyWith(key).build().parseSignedClaims(token);

// after (build with jjwt so alg is set automatically)
String token = Jwts.builder().subject("me")
    .signWith(key, Jwts.SIG.HS256).compact();
Defensive patterns

Strategy: validation

Validate before calling

String[] parts = token.split("\\.");
String headerJson = new String(java.util.Base64.getUrlDecoder().decode(parts[0]), java.nio.charset.StandardCharsets.UTF_8);
if (!headerJson.contains("alg") || io.jsonwebtoken.lang.Strings.hasText(headerJson.replace("alg", "").trim()) == false) throw new IllegalArgumentException("token header missing alg");

Try / catch

try { return parser.parseSignedClaims(token); }
catch (io.jsonwebtoken.MalformedJwtException e) { log.warn("Token missing alg header", e); throw new InvalidTokenException(e); }

Prevention

When it happens

Trigger: Calling JwtParser.parse(), parseSignedContent(), or parseSignedClaims() on a compact token whose protected header either omits 'alg' entirely or has an empty/whitespace-only 'alg' value.

Common situations: Hand-constructed or manually base64url-encoded tokens; tokens produced by a custom or buggy serializer that drops the alg field; truncated/corrupted tokens where header JSON was altered; third-party token issuers that emit non-compliant headers.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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