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
- Send a standard 'Authorization: Bearer <token>' header with a JWT or supported access token
- Check the token is well-formed (three dot-separated JWT segments) and not base64/percent-encoded oddly
- Confirm you are calling the endpoint intended for that credential type (Heimdall policies expect principal-bearing tokens)
- 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
- Always use the standard Bearer scheme
- Validate token shape client-side before sending
- Use the correct credential type for the endpoint
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
- Denied
- Cannot authorize principal
- Unable to accept the ID token with an invalid [sub] claim
- Unauthorized account removal attempt
- Token has expired: and is after
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)