{"record":{"id":"082d7649864397c7","repo":"apereo/cas","slug":"requested-grant-type-is-not-authorized-by-ser-082d76","errorCode":null,"errorMessage":"Requested grant type [{}] is not authorized by service definition [{}]","messagePattern":"Requested grant type \\[(.+?)\\] is not authorized by service definition \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-oauth-core-api/src/main/java/org/apereo/cas/support/oauth/validator/token/OAuth20TokenExchangeGrantTypeTokenRequestValidator.java","lineNumber":59,"sourceCode":"    protected boolean validateInternal(final WebContext webContext, final String grantType,\n                                       final ProfileManager manager, final UserProfile uProfile) throws Throwable {\n        val configurationContext = getConfigurationContext().getObject();\n        val requestParameterResolver = configurationContext.getRequestParameterResolver();\n        val subjectTokenType = requestParameterResolver.resolveRequestParameter(webContext, OAuth20Constants.SUBJECT_TOKEN_TYPE)\n            .orElseThrow(() -> new IllegalArgumentException(\"Subject token type cannot be undefined\"));\n        val subjectToken = requestParameterResolver.resolveRequestParameter(webContext, OAuth20Constants.SUBJECT_TOKEN)\n            .orElseThrow(() -> new IllegalArgumentException(\"Subject token cannot be undefined\"));\n        val requestedTokenType = requestParameterResolver.resolveRequestParameter(webContext, OAuth20Constants.REQUESTED_TOKEN_TYPE)\n            .orElseGet(OAuth20TokenExchangeTypes.ACCESS_TOKEN::getType);\n\n        val registeredService = extractRegisteredService(subjectTokenType, subjectToken);\n\n        val audit = AuditableContext.builder().registeredService(registeredService).build();\n        val accessResult = configurationContext.getRegisteredServiceAccessStrategyEnforcer().execute(audit);\n        accessResult.throwExceptionIfNeeded();\n\n        if (!isGrantTypeSupportedBy(Objects.requireNonNull(registeredService), getGrantType().getType(), true)) {\n            LOGGER.warn(\"Requested grant type [{}] is not authorized by service definition [{}]\",\n                grantType, Objects.requireNonNull(registeredService).getServiceId());\n            return false;\n        }\n\n        val actorToken = requestParameterResolver.resolveRequestParameter(webContext, OAuth20Constants.ACTOR_TOKEN);\n        val actorTokenType = requestParameterResolver.resolveRequestParameter(webContext, OAuth20Constants.ACTOR_TOKEN_TYPE).map(OAuth20TokenExchangeTypes::from);\n        FunctionUtils.throwIf(actorToken.isPresent() && actorTokenType.isEmpty(),\n            () -> new IllegalArgumentException(\"Actor token type cannot be undefined when actor token is provided\"));\n\n        val resourceAndAudience = requestParameterResolver.resolveRequestParameters(\n            List.of(OAuth20Constants.RESOURCE, OAuth20Constants.AUDIENCE), webContext);\n        val resources = resourceAndAudience.getOrDefault(OAuth20Constants.RESOURCE, Set.of());\n        val audience = resourceAndAudience.getOrDefault(OAuth20Constants.AUDIENCE, Set.of());\n        val tokenExchangePolicy = registeredService.getTokenExchangePolicy();\n        return tokenExchangePolicy == null || tokenExchangePolicy.isTokenExchangeAllowed(registeredService, resources, audience, requestedTokenType);\n    }\n\n    protected @Nullable OAuthRegisteredService extractRegisteredService(final String subjectTokenType,","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-oauth-core-api/src/main/java/org/apereo/cas/support/oauth/validator/token/OAuth20TokenExchangeGrantTypeTokenRequestValidator.java#L41-L77","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before (service JSON)\n\"supportedGrantTypes\": [\"client_credentials\"]\n// after\n\"supportedGrantTypes\": [\"client_credentials\", \"urn:ietf:params:oauth:grant-type:token-exchange\"]","handlingStrategy":"validation","validationCode":"const TOKEN_EXCHANGE = 'urn:ietf:params:oauth:grant-type:token-exchange';\nif (!service.supportedGrantTypes?.includes(TOKEN_EXCHANGE)) {\n  throw new Error('token-exchange grant not authorized for this service definition');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["oauth","token-exchange","grant-type","rfc8693"],"backgroundTag":"unsupported-operation","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}