apereo/cas · warning
Private key JWT authentication method is not enabled for…
Error message
Private key JWT authentication method is not enabled for CAS, or is not supported for service [{}] What it means
CAS's OIDC JWT authenticator (used for client authentication at the token endpoint via client_secret_jwt / private_key_jwt) first checks that the server discovery document advertises these JWT auth methods and that the specific registered service allows one of them. If either fails, it refuses the credentials and returns empty, logging this warning.
Solutions
- Set the registered service's tokenEndpointAuthenticationMethod to client_secret_jwt or private_key_json (private_key_jwt) for that client.
- Ensure cas.authn.oidc.discovery.token-endpoint-auth-methods-supported includes client_secret_jwt and/or private_key_jwt.
- If JWT auth is intentionally disabled, switch the client SDK to client_secret_basic or client_secret_post instead.
- Reload/restart after changing service registration so ServicesManager picks up the new auth method.
Example fix
// before (service JSON) "tokenEndpointAuthenticationMethod": "client_secret_basic" // after "tokenEndpointAuthenticationMethod": "private_key_json"
Defensive patterns
Strategy: validation
Validate before calling
// fetch discovery and check supported auth methods before using JWT auth
const disco = await fetch(`${issuer}/.well-known/openid-configuration`).then(r => r.json());
if (!disco.token_endpoint_auth_methods_supported?.some(m => m.endsWith('_jwt')))
throw new Error('JWT client auth not supported by this CAS deployment'); Prevention
- Align the service registration's tokenEndpointAuthenticationMethod with the client SDK's setting.
- Check discovery metadata when configuring clients.
- Keep discovery tokenEndpointAuthMethodsSupported defaults unless intentionally restricting.
When it happens
Trigger: A client authenticates to the OIDC token endpoint using client_secret_jwt or private_key_jwt while either cas.authn.oidc.discovery.tokenEndpointAuthMethodsSupported excludes both JWT methods, or the registered service's tokenEndpointAuthenticationMethod is set to something else so OAuth20Utils.isTokenAuthenticationMethodSupportedFor fails.
Common situations: Client SDK defaults to private_key_jwt but the service registration says client_secret_basic; deployment overrode the discovery supported-auth-methods list; service JSON copied from another client with a different authentication method.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Unable to verify credentials
- Unable to use 'none' for the user-info signing algorithm
- Unable to use 'none' as user-info encryption algorithm
- Service with client id is configured to encrypt tokens, yet…
- Unable to verify JWT assertion with any of the configured…
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/c1a331fe8ce47a7f.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/authn/OidcJwtAuthenticator.java:118
return null;
}
@Override
public Optional<Credentials> validate(final CallContext callContext, final Credentials creds) {
return FunctionUtils.doAndHandle(() -> {
val registeredService = getOidcRegisteredService(callContext);
RegisteredServiceAccessStrategyUtils.ensureServiceAccessIsAllowed(registeredService);
Objects.requireNonNull(registeredService, "regisetered service is null");
if (OAuth20Utils.isAccessTokenRequest(callContext.webContext())) {
val authMethodDisabled = oidcServerDiscoverySettings.getTokenEndpointAuthMethodsSupported()
.stream()
.map(OAuth20ClientAuthenticationMethods::parse)
.noneMatch(method -> method == OAuth20ClientAuthenticationMethods.CLIENT_SECRET_JWT || method == OAuth20ClientAuthenticationMethods.PRIVATE_KEY_JWT);
if (authMethodDisabled || !OAuth20Utils.isTokenAuthenticationMethodSupportedFor(callContext, registeredService,
OAuth20ClientAuthenticationMethods.CLIENT_SECRET_JWT, OAuth20ClientAuthenticationMethods.PRIVATE_KEY_JWT)) {
LOGGER.warn("Private key JWT authentication method is not enabled for CAS, or is not supported for service [{}]", registeredService.getName());
return Optional.<Credentials>empty();
}
}
val credentials = (UsernamePasswordCredentials) creds;
val jwt = verifyCredentials(credentials, callContext.webContext());
if (jwt == null) {
LOGGER.warn("Unable to verify credentials");
return Optional.<Credentials>empty();
}
val keys = new JsonWebKeySet();
clientJwksRegistrationStore.ifAvailable(Unchecked.consumer(store -> {
if (jwt instanceof final SignedJWT signedJWT) {
val jwk = signedJWT.getHeader().getJWK();
val kid = signedJWT.getHeader().getKeyID();
val jkt = jwk != null ? jwk.computeThumbprint().toString() : StringUtils.EMPTY;
store.findBy(registeredService.getClientId(), jkt)View on GitHub (pinned to e7288fc434)