@Override
    protected Authentication getActorTokenAuthentication(final WebContext webContext, final TokenExchangeRequest extractedRequest) throws Throwable {
        val configurationContext = getConfigurationContext().getObject();
        val actorToken = configurationContext.getRequestParameterResolver()
            .resolveRequestParameter(webContext, OAuth20Constants.ACTOR_TOKEN);
        val actorTokenType = configurationContext.getRequestParameterResolver()
            .resolveRequestParameter(webContext, OAuth20Constants.ACTOR_TOKEN_TYPE)
            .map(OAuth20TokenExchangeTypes::from);

        if (actorTokenType.isPresent() && actorTokenType.get() == OAuth20TokenExchangeTypes.DEVICE_SECRET) {
            val subjectTokenType = configurationContext.getRequestParameterResolver()
                .resolveRequestParameter(webContext, OAuth20Constants.SUBJECT_TOKEN_TYPE)
                .map(OAuth20TokenExchangeTypes::from);
            FunctionUtils.throwIf(subjectTokenType.isEmpty() || subjectTokenType.get() != OAuth20TokenExchangeTypes.ID_TOKEN,
                () -> new IllegalArgumentException("Subject token type is missing or not an ID token when actor token is %s".formatted(actorTokenType.get())));
            val subjectToken = configurationContext.getRequestParameterResolver()
                .resolveRequestParameter(webContext, OAuth20Constants.SUBJECT_TOKEN)
                .orElseThrow(() -> new IllegalArgumentException("Subject token type cannot be undefined when actor token is provided"));
            val clientIdInIdToken = OAuth20Utils.extractClientIdFromToken(subjectToken);
            val registeredService = OAuth20Utils.getRegisteredOAuthServiceByClientId(configurationContext.getServicesManager(), clientIdInIdToken);
            val claims = configurationContext.getIdTokenSigningAndEncryptionService().decode(subjectToken, Optional.ofNullable(registeredService));
            Assert.isTrue(claims.hasClaim(OidcConstants.DS_HASH), "Subject token must contain the claim %s".formatted(OidcConstants.DS_HASH));
            Assert.isTrue(claims.hasClaim(OidcConstants.CLAIM_SESSION_ID), "Subject token must contain the claim %s".formatted(OidcConstants.CLAIM_SESSION_ID));
            Assert.isTrue(claims.hasClaim(OidcConstants.CLAIM_SESSION_REF), "Subject token must contain the claim %s".formatted(OidcConstants.CLAIM_SESSION_REF));

            val deviceSecret = configurationContext.getDeviceSecretGenerator().hash(actorToken.orElseThrow());
            Assert.isTrue(StringUtils.equals(deviceSecret, claims.getStringClaimValue(OidcConstants.DS_HASH)),
                "Device secret hash does not match the subject token claim %s".formatted(OidcConstants.DS_HASH));

            val base64DecodedSessionId = EncodingUtils.decodeUrlSafeBase64(claims.getStringClaimValue(OidcConstants.CLAIM_SESSION_REF));
            val ticketGrantingTicketId = new String((byte[]) configurationContext.getTicketRegistry().getCipherExecutor().decode(base64DecodedSessionId), StandardCharsets.UTF_8);
            val ticketGrantingTicket = configurationContext.getTicketRegistry().getTicket(ticketGrantingTicketId, TicketGrantingTicket.class);
            Assert.notNull(ticketGrantingTicket, "Ticket granting ticket cannot be null");
            return ticketGrantingTicket.getAuthentication();
        }
        return super.getActorTokenAuthentication(webContext, extractedRequest);
    }
This is {@link OidcAccessTokenTokenExchangeGrantRequestExtractor}.

@author Misagh Moayyed
@since 7.3.0
support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/token/OidcAccessTokenTokenExchangeGrantRequestExtractor.java