{"record":{"id":"ea5be0113f628ef8","repo":"apereo/cas","slug":"actor-token-type-s-is-not-supported","errorCode":null,"errorMessage":"Actor token type %s is not supported","messagePattern":"Actor token type (.+?) is not supported","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"support/cas-server-support-oauth-core-api/src/main/java/org/apereo/cas/support/oauth/web/response/accesstoken/ext/AccessTokenTokenExchangeGrantRequestExtractor.java","lineNumber":159,"sourceCode":"            .resolveRequestParameter(webContext, OAuth20Constants.ACTOR_TOKEN).orElseThrow();\n        val actorTokenType = configurationContext.getRequestParameterResolver()\n            .resolveRequestParameter(webContext, OAuth20Constants.ACTOR_TOKEN_TYPE)\n            .map(OAuth20TokenExchangeTypes::from)\n            .orElseThrow();\n        return buildActorTokenAuthentication(webContext, actorTokenType, actorToken);\n    }\n\n    protected Authentication buildActorTokenAuthentication(final WebContext webContext,\n                                                           final OAuth20TokenExchangeTypes actorTokenType,\n                                                           final String actorToken) throws Throwable {\n        val configurationContext = getConfigurationContext().getObject();\n        return switch (actorTokenType) {\n            case ACCESS_TOKEN -> {\n                val token = configurationContext.getTicketRegistry().getTicket(actorToken, OAuth20Token.class);\n                yield Objects.requireNonNull(token.getAuthentication());\n            }\n            case JWT -> buildActorTokenAuthenticationFromJwt(actorToken, webContext);\n            default -> throw new IllegalArgumentException(\"Actor token type %s is not supported\".formatted(actorTokenType));\n        };\n    }\n\n    protected Authentication buildActorTokenAuthenticationFromJwt(final String actorToken, final WebContext webContext) throws Throwable {\n        val configurationContext = getConfigurationContext().getObject();\n        val claimSet = configurationContext.getAccessTokenJwtBuilder().unpack(Optional.empty(), actorToken);\n        val service = configurationContext.getWebApplicationServiceServiceFactory().createService(claimSet.getIssuer());\n        service.getAttributes().put(OAuth20Constants.CLIENT_ID, List.of(claimSet.getIssuer()));\n        val registeredService = OAuth20Utils.getRegisteredOAuthServiceByClientId(configurationContext.getServicesManager(), claimSet.getIssuer());\n        val userProfile = extractUserProfile(webContext).orElseThrow();\n        return configurationContext.getAuthenticationBuilder().build(userProfile, registeredService, webContext, service);\n    }\n\n    public record TokenExchangeRequest(Serializable token, Service service,\n        OAuthRegisteredService registeredService, Authentication authentication) {\n    }\n}\n","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-oauth-core-api/src/main/java/org/apereo/cas/support/oauth/web/response/accesstoken/ext/AccessTokenTokenExchangeGrantRequestExtractor.java#L141-L177","documentation":"The actor_token in an RFC 8693 token-exchange request may only be an existing CAS OAuth access token or a JWT. Any other actor_token_type reaches the default branch of the switch and throws IllegalArgumentException, rejecting the delegation portion of the exchange.","triggerScenarios":"Including actor_token/actor_token_type in a token-exchange request with a type other than ACCESS_TOKEN or JWT (e.g. refresh token or SAML assertion) while calling extractActorTokenAuthentication via the extractor flow.","commonSituations":"Clients attempting actor impersonation/delegation with unsupported token formats; misconfigured client sends actor_token without the matching actor_token_type; older client libraries emitting deprecated actor token types.","solutions":["Omit actor_token entirely if delegation is not needed","Use actor_token_type=urn:ietf:params:oauth:token-type:access_token with a valid CAS OAuth access token","Use actor_token_type=urn:ietf:params:oauth:token-type:jwt with a JWT unpackable by the configured access-token JWT builder","Confirm the actor token is still live in the ticket registry (expired tickets won't resolve for ACCESS_TOKEN either)","Subclass the extractor and override buildActorTokenAuthentication if a custom actor token type is required"],"exampleFix":"// before\n-d actor_token=RT-1234 -d actor_token_type=urn:ietf:params:oauth:token-type:refresh_token\n// after\n-d actor_token=AT-1234 -d actor_token_type=urn:ietf:params:oauth:token-type:access_token","handlingStrategy":"validation","validationCode":"Set<String> supported = Set.of(\"urn:ietf:params:oauth:token-type:access_token\",\"urn:ietf:params:oauth:token-type:jwt\");\nif (actorToken != null && !supported.contains(actorTokenType)) throw new IllegalArgumentException(\"Unsupported actor_token_type\");","typeGuard":"boolean isSupportedActorTokenType(String t) { return t == null || Set.of(\"urn:ietf:params:oauth:token-type:access_token\",\"urn:ietf:params:oauth:token-type:jwt\").contains(t); }","tryCatchPattern":"try { exchange(...); } catch (IllegalArgumentException e) { return ResponseEntity.badRequest().body(Map.of(\"error\",\"unsupported_token_type\",\"description\",\"actor_token_type\")); }","preventionTips":["Only attach actor_token when delegation is actually required","Keep actor token in the same format as the subject token (access token or JWT)","Verify actor tokens are live CAS tickets before sending"],"tags":["oauth","token-exchange","actor-token","unsupported-value"],"backgroundTag":"unsupported-enum-value","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"}