apereo/cas · warning
Requested grant type
Error message
Requested grant type [{}] is not authorized by service definition [{}] What it means
For the token-exchange (RFC 8693) grant, OAuth20TokenExchangeGrantTypeTokenRequestValidator checks isGrantTypeSupportedBy(registeredService, type, true) after enforcing the access strategy. If the registered service does not explicitly authorize the token-exchange grant type, the validator warns and returns false, rejecting the exchange.
Solutions
- Add "urn:ietf:params:oauth:grant-type:token-exchange" to the service definition's supportedGrantTypes.
- Reload the services registry after updating the definition.
- Verify the client's request uses the exact token-exchange grant_type URI.
- Review CAS docs for any additional token-exchange prerequisites (allowed actors/subject token types) if the error persists.
Example fix
// before (service JSON) "supportedGrantTypes": ["client_credentials"] // after "supportedGrantTypes": ["client_credentials", "urn:ietf:params:oauth:grant-type:token-exchange"]
Defensive patterns
Strategy: validation
Validate before calling
const TOKEN_EXCHANGE = 'urn:ietf:params:oauth:grant-type:token-exchange';
if (!service.supportedGrantTypes?.includes(TOKEN_EXCHANGE)) {
throw new Error('token-exchange grant not authorized for this service definition');
} Prevention
- Explicitly list the token-exchange grant URI on services using RFC 8693
- Send the grant_type string exactly (full URN, case-sensitive parameters)
- Review token-exchange docs for actor/subject token prerequisites
When it happens
Trigger: A POST to the token endpoint with grant_type=urn:ietf:params:oauth:grant-type:token-exchange whose clientId resolves to a service definition lacking that grant type in its authorized grant types.
Common situations: Service configured for authorization_code only; token exchange newly adopted and service definitions not updated; misunderstanding that token exchange is allowed by default (it must be listed).
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Subject token type is not supported
- Actor token type is not supported
- Response type [ ] or grant type [ ] is not supported.
- Requested grant type
- Requested grant type
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/082d7649864397c7.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oauth-core-api/src/main/java/org/apereo/cas/support/oauth/validator/token/OAuth20TokenExchangeGrantTypeTokenRequestValidator.java:59
protected boolean validateInternal(final WebContext webContext, final String grantType,
final ProfileManager manager, final UserProfile uProfile) throws Throwable {
val configurationContext = getConfigurationContext().getObject();
val requestParameterResolver = configurationContext.getRequestParameterResolver();
val subjectTokenType = requestParameterResolver.resolveRequestParameter(webContext, OAuth20Constants.SUBJECT_TOKEN_TYPE)
.orElseThrow(() -> new IllegalArgumentException("Subject token type cannot be undefined"));
val subjectToken = requestParameterResolver.resolveRequestParameter(webContext, OAuth20Constants.SUBJECT_TOKEN)
.orElseThrow(() -> new IllegalArgumentException("Subject token cannot be undefined"));
val requestedTokenType = requestParameterResolver.resolveRequestParameter(webContext, OAuth20Constants.REQUESTED_TOKEN_TYPE)
.orElseGet(OAuth20TokenExchangeTypes.ACCESS_TOKEN::getType);
val registeredService = extractRegisteredService(subjectTokenType, subjectToken);
val audit = AuditableContext.builder().registeredService(registeredService).build();
val accessResult = configurationContext.getRegisteredServiceAccessStrategyEnforcer().execute(audit);
accessResult.throwExceptionIfNeeded();
if (!isGrantTypeSupportedBy(Objects.requireNonNull(registeredService), getGrantType().getType(), true)) {
LOGGER.warn("Requested grant type [{}] is not authorized by service definition [{}]",
grantType, Objects.requireNonNull(registeredService).getServiceId());
return false;
}
val actorToken = requestParameterResolver.resolveRequestParameter(webContext, OAuth20Constants.ACTOR_TOKEN);
val actorTokenType = requestParameterResolver.resolveRequestParameter(webContext, OAuth20Constants.ACTOR_TOKEN_TYPE).map(OAuth20TokenExchangeTypes::from);
FunctionUtils.throwIf(actorToken.isPresent() && actorTokenType.isEmpty(),
() -> new IllegalArgumentException("Actor token type cannot be undefined when actor token is provided"));
val resourceAndAudience = requestParameterResolver.resolveRequestParameters(
List.of(OAuth20Constants.RESOURCE, OAuth20Constants.AUDIENCE), webContext);
val resources = resourceAndAudience.getOrDefault(OAuth20Constants.RESOURCE, Set.of());
val audience = resourceAndAudience.getOrDefault(OAuth20Constants.AUDIENCE, Set.of());
val tokenExchangePolicy = registeredService.getTokenExchangePolicy();
return tokenExchangePolicy == null || tokenExchangePolicy.isTokenExchangeAllowed(registeredService, resources, audience, requestedTokenType);
}
protected @Nullable OAuthRegisteredService extractRegisteredService(final String subjectTokenType,View on GitHub (pinned to e7288fc434)