quarkusio/quarkus · error · org.jose4j.jwt.MalformedClaimException

expected claim must be a list of strings

Error message

expected claim %s must be a list of strings

What it means

Thrown by OidcProvider's CustomClaimsValidator when a required claim configured in quarkus.oidc.token.required-claims is expected to be a single string value in the token, but the app configured MULTIPLE required values for that claim (a set), and the token claim is a plain string. A string claim can only be compared against exactly one expected value, so the configuration is contradictory and MalformedClaimException is thrown.

Solutions

  1. Configure only ONE required value per claim if the token emits it as a plain string
  2. If multiple alternatives are needed, the token claim must be a JSON array of strings — ask the issuer to emit e.g. "acr": ["loa2","loa3"]
  3. Match exactly the value the token actually carries (decode the token to inspect the claim type)
  4. Use a custom Validator bean instead of static config if flexible matching is required

Example fix

# before (token acr is a plain string "loa2")
quarkus.oidc.token.required-claims.acr=loa2,loa3
# after
quarkus.oidc.token.required-claims.acr=loa2
Defensive patterns

Strategy: validation

Validate before calling

// decode the token and check the claim type before configuring multiple expected values
var claims = decodeJwtClaims(token);
Object acr = claims.get("acr");
if (!(acr instanceof List)) {
    // claim is a plain string: configure exactly ONE required value
}

Type guard

boolean isStringOrStringList(Object v) {
    return v instanceof String || (v instanceof List<?> l && l.stream().allMatch(String.class::isInstance));
}

Prevention

When it happens

Trigger: quarkus.oidc.token.required-claims.<name> maps to a set with 2+ values (e.g. comma-separated list) while the verified token's claim with that name is a single string, not a JSON array of strings.

Common situations: Developers write required-claims.acr=loa2,loa3 expecting OR-matching, but the provider emits acr as the string 'loa2' rather than an array; misunderstanding that multiple values only work when the token claim is a string list.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/2395ef5fb5461d98. Report an issue: GitHub.

Appendix: source

Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProvider.java:746

                }
            }
            return null;
        }

        private static String validate(String requiredClaimName, Set<String> requiredClaimValues, JwtClaims claims)
                throws MalformedClaimException {
            if (!claims.hasClaim(requiredClaimName)) {
                return "claim " + requiredClaimName + " is missing";
            }
            if (claims.isClaimValueString(requiredClaimName)) {
                if (requiredClaimValues.size() == 1) {
                    String actualClaimValue = claims.getStringClaimValue(requiredClaimName);
                    String requiredClaimValue = requiredClaimValues.iterator().next();
                    if (!requiredClaimValue.equals(actualClaimValue)) {
                        return "claim " + requiredClaimName + " does not match expected value of " + requiredClaimValues;
                    }
                } else {
                    throw new MalformedClaimException("expected claim " + requiredClaimName + " must be a list of strings");
                }
            } else {
                if (claims.isClaimValueStringList(requiredClaimName)) {
                    List<String> actualClaimValues = claims.getStringListClaimValue(requiredClaimName);
                    for (String requiredClaimValue : requiredClaimValues) {
                        if (!actualClaimValues.contains(requiredClaimValue)) {
                            return "claim " + requiredClaimName + " does not match expected value of " + requiredClaimValues;
                        }
                    }
                } else {
                    throw new MalformedClaimException(
                            "expected claim " + requiredClaimName + " must be a list of strings or a string");
                }
            }
            return null;
        }
    }

View on GitHub (pinned to e1c734241f)