apereo/cas · error
unauthorized_client
unauthorized_client
Error message
Client is not allowed to use the [%s] response type
What it means
Even when a response_type value is globally supported, the registered OAuth service must be permitted to use it. DefaultOAuth20RequestParameterResolver.isAuthorizedResponseTypeForService checks the service's supported grant types / response-type configuration; if the code flow's response_type is not authorized for this client, CAS fails the authorization request with error=unauthorized_client.
Solutions
- Set the service's supported grant types to include AUTHORIZATION_CODE (e.g. supportedGrantTypes in the registered service definition) via the service registry or management app.
- Confirm no custom OAuth20RequestParameterResolver bean overrides isAuthorizedResponseTypeForService in a way that rejects code for this service.
- Re-export/recreate the registered service JSON so grant-type fields survive upgrades; check cas.authn.oauth defaults if relying on global fallback.
- Use the response_type/grant type actually allowed for the client (e.g. token for implicit-only clients).
Example fix
// before (registered service JSON) "supportedGrantTypes": ["CLIENT_CREDENTIALS"] // after "supportedGrantTypes": ["AUTHORIZATION_CODE", "REFRESH_TOKEN"]
Defensive patterns
Strategy: validation
Validate before calling
// before initiating code flow, confirm the client's registry entry permits it
if (!service.supportedGrantTypes?.includes('AUTHORIZATION_CODE')) {
throw new Error('Client not authorized for authorization_code response type');
} Prevention
- When registering an OAuth service in CAS, always set supportedGrantTypes/supportedResponseTypes explicitly to the flows the client uses.
- Audit service-registry JSON after CAS upgrades for dropped grant-type fields.
- Keep one service definition per client rather than sharing broad grant types.
When it happens
Trigger: GET /oauth2.0/authorize?response_type=code&client_id=X where registered service X does not list the authorization-code grant type (or its supportedGrantTypes/responseTypes config excludes it), or a custom OAuth20RequestParameterResolver bean denies it.
Common situations: Service registered for implicit/client-credentials only but client initiates code flow; service definition missing supportedGrantTypes after migration from an older CAS where defaults were broader; JSON service exported without the supportedGrantTypes/supportedResponseTypes fields.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- unsupported_response_type
- Response type [ ] or grant type [ ] is not supported.
- Registered service access is not allowed for service…
- Provided client id [ ] cannot be matched against a service…
- Response type not authorized for service
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/34159fb0539e40cd.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oauth-core-api/src/main/java/org/apereo/cas/support/oauth/validator/authorization/OAuth20AuthorizationCodeResponseTypeAuthorizationRequestValidator.java:51
final ServicesManager servicesManager,
final ServiceFactory<WebApplicationService> webApplicationServiceServiceFactory,
final AuditableExecution registeredServiceAccessStrategyEnforcer,
final OAuth20RequestParameterResolver requestParameterResolver) {
super(servicesManager, webApplicationServiceServiceFactory,
registeredServiceAccessStrategyEnforcer, requestParameterResolver);
}
@Override
public boolean validate(final WebContext context) {
val clientIdResult = requestParameterResolver.resolveRequestParameter(context, OAuth20Constants.CLIENT_ID);
return clientIdResult
.map(this::getRegisteredServiceByClientId)
.filter(Objects::nonNull)
.map(registeredService -> {
if (!requestParameterResolver.isAuthorizedResponseTypeForService(context, registeredService)) {
val responseTypeResult = requestParameterResolver.resolveRequestParameter(context, OAuth20Constants.RESPONSE_TYPE);
val msg = String.format("Client is not allowed to use the [%s] response type", responseTypeResult.orElse("unknown"));
LOGGER.warn(msg);
setErrorDetails(context, OAuth20Constants.UNAUTHORIZED_CLIENT, msg, true);
return false;
}
return true;
})
.orElse(false);
}
@Override
public boolean supports(final WebContext context) throws Throwable {
if (preValidate(context)) {
val responseType = requestParameterResolver.resolveRequestParameter(context, OAuth20Constants.RESPONSE_TYPE)
.map(String::valueOf)
.orElse(StringUtils.EMPTY);
LOGGER.debug("Requested response type is [{}]", responseType);
return getSupportedResponseTypes()
.stream()
.anyMatch(allowedType -> OAuth20Utils.isResponseType(responseType, allowedType));View on GitHub (pinned to e7288fc434)