apereo/cas · error · AuthenticationException

Unknown authorization header type

Error message

Unknown authorization header type

What it means

DefaultAuthorizationPrincipalParser (Heimdall) inspects the Authorization header to decide its token type: Bearer access token, JWT, or signed authorization assertion. If the header does not fit any recognized type, it throws AuthenticationException 'Unknown authorization header type'.

Solutions

  1. Send a standard 'Authorization: Bearer <token>' header with a JWT or supported access token
  2. Check the token is well-formed (three dot-separated JWT segments) and not base64/percent-encoded oddly
  3. Confirm you are calling the endpoint intended for that credential type (Heimdall policies expect principal-bearing tokens)
  4. Inspect DefaultAuthorizationPrincipalParser for the accepted header formats and match your client accordingly

Example fix

// before
Authorization: Apitoken abc123
// after
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Defensive patterns

Strategy: validation

Validate before calling

String header = request.getHeader("Authorization");
if (header == null || !header.startsWith("Bearer ") || !header.substring(7).contains(".")) {
    throw new IllegalArgumentException("Authorization header must be 'Bearer <jwt>'");
}

Type guard

static boolean isBearerJwt(String header) {
    return header != null && header.startsWith("Bearer ")
        && header.substring(7).split("\\.").length == 3;
}

Try / catch

try {
    return parser.claims(token);
} catch (AuthenticationException e) {
    // return 401 with WWW-Authenticate: Bearer
}

Prevention

When it happens

Trigger: parseAuthorizationHeader (via claims()) receives a header whose scheme/prefix is not one of the handled forms, so all parse branches are skipped and the final throw is reached.

Common situations: Client sends a custom scheme (e.g. 'Token abc') instead of Bearer; header contains an opaque key or API key that is not a JWT; missing 'Bearer ' prefix so the token is misparsed; sending the wrong kind of credential to the Heimdall endpoint.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/a8d1c4dfaa0e21ac. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-heimdall/src/main/java/org/apereo/cas/heimdall/engine/DefaultAuthorizationPrincipalParser.java:88

        }
        return PrincipalFactoryUtils.newPrincipalFactory().createPrincipal(claims.getSubject(), principalAttributes);
    }

    protected JWTClaimsSet parseAuthorizationHeader(final String authorizationHeader) throws Throwable {
        if (authorizationHeader.startsWith("Basic ")) {
            val token = Strings.CI.removeStart(authorizationHeader, "Basic ");
            return buildClaimSetFromAuthentication(token);
        }
        if (authorizationHeader.startsWith("Bearer ")) {
            val token = Strings.CI.removeStart(authorizationHeader, "Bearer ");
            val claims = parseOidcIdToken(token)
                .or(() -> parseJwtAccessToken(token))
                .or(() -> getJwtClaimsSetFromAccessToken(token))
                .or(() -> parseJwtAuthorization(token))
                .orElseThrow(() -> new AuthenticationException("Unable to parse and verify token"));
            return validateClaims(claims);
        }
        throw new AuthenticationException("Unknown authorization header type");
    }

    protected Optional<JWTClaimsSet> parseJwtAuthorization(final String token){
        try {
            val clientIdInAssertion = OAuth20Utils.extractClientIdFromToken(token);
            LOGGER.debug("Client id retrieved from ID token is [{}]", clientIdInAssertion);
            val registeredService = OAuth20Utils.getRegisteredOAuthServiceByClientId(
                accessTokenJwtBuilder.getObject().getServicesManager(),
                clientIdInAssertion, OidcRegisteredService.class);

            val jsonWebKeys = getJsonWebKeyToVerifyAssertion(registeredService);
            val verifiedAssertion = verifyAssertion(token, jsonWebKeys);
            val claims = JwtClaims.parse(verifiedAssertion);

            val baseOidcUrl = accessTokenJwtBuilder.getObject().getCasProperties()
                .getServer().getPrefix() + '/' + OidcConstants.BASE_OIDC_URL + '/';
            val jwtClaimsSetVerifier = new DefaultJWTClaimsVerifier<>(
                CollectionUtils.wrapSet(

View on GitHub (pinned to e7288fc434)