apereo/cas · warning

Requested grant type

Error message

Requested grant type [{}] is not authorized by service definition [{}]

What it means

After confirming the refresh token exists, OAuth20RefreshTokenGrantTypeTokenRequestValidator checks that the registered service authorizes the refresh-token grant type via isGrantTypeSupportedBy. If the service definition's supported grant types do not include the refresh_token grant, the validator warns and returns false, denying the token refresh.

Solutions

  1. Add "urn:ietf:params:oauth:grant-type:refresh_token" to the service's supportedGrantTypes.
  2. Reload/redeploy the service registry so ServicesManager sees the update.
  3. Audit all clients that perform token refresh to ensure their service definitions list the grant type.
  4. Verify no service-matching ambiguity causes a different (wrong) service definition to be selected for this clientId.

Example fix

// before (service JSON)
"supportedGrantTypes": ["authorization_code"]
// after
"supportedGrantTypes": ["authorization_code", "refresh_token"]
Defensive patterns

Strategy: validation

Validate before calling

const REFRESH_GRANT = 'urn:ietf:params:oauth:grant-type:refresh_token';
if (!service.supportedGrantTypes?.includes(REFRESH_GRANT)) {
  throw new Error('refresh_token grant not authorized for this service definition');
}

Prevention

When it happens

Trigger: A grant_type=refresh_token request whose clientId resolves to a service with an empty or non-matching supportedGrantTypes list (missing urn:ietf:params:oauth:grant-type:refresh_token).

Common situations: Service definition authored with only authorization_code in supportedGrantTypes; registry entry edited without reloading; deployment upgraded to a CAS version that enforces grant-type authorization strictly.

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


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/d46b2c789c810d0b. 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/OAuth20RefreshTokenGrantTypeTokenRequestValidator.java:65

        try {
            refreshToken = configurationContext.getTicketRegistry().getTicket(token, OAuth20RefreshToken.class);
            LOGGER.trace("Found valid refresh token [{}] in the registry", refreshToken);
        } catch (final InvalidTicketException e) {
            LOGGER.warn("Provided refresh token [{}] cannot be found in the registry or has expired", token);
            return false;
        }

        LOGGER.debug("Received grant type [{}] with client id [{}]", grantType, clientId);
        val registeredService = OAuth20Utils.getRegisteredOAuthServiceByClientId(
            configurationContext.getServicesManager(), clientId);
        val audit = AuditableContext.builder()
            .registeredService(registeredService)
            .build();
        val accessResult = configurationContext.getRegisteredServiceAccessStrategyEnforcer().execute(audit);
        accessResult.throwExceptionIfNeeded();

        if (!isGrantTypeSupportedBy(Objects.requireNonNull(registeredService), grantType)) {
            LOGGER.warn("Requested grant type [{}] is not authorized by service definition [{}]",
                grantType, Objects.requireNonNull(registeredService).getServiceId());
            return false;
        }

        if (refreshToken != null && !Strings.CI.equals(refreshToken.getClientId(), clientId)) {
            LOGGER.warn("Provided refresh token [{}] does not belong to client [{}]", refreshToken.getId(), clientId);
            return false;
        }

        return true;
    }

    @Override
    protected OAuth20GrantTypes getGrantType() {
        return OAuth20GrantTypes.REFRESH_TOKEN;
    }
}

View on GitHub (pinned to e7288fc434)