apereo/cas · warning
missing_access_token
missing_access_token
Error message
Access token cannot be found in the request
What it means
The introspection request passed client authentication checks but did not include the access token to introspect. CAS requires either a 'token' or 'access_token' request parameter; when neither is present it returns a 400 with error 'missing_access_token' (OAuth 2.0 Token Introspection RFC 7662 requires the token parameter).
Solutions
- Include the token as a form parameter: token=<access-token> in the introspection POST body
- Alternatively use the access_token parameter name
- Send the request as application/x-www-form-urlencoded so parameters are parsed
- Do not rely on the Authorization: Bearer header to carry the token to introspect
Example fix
// before curl -u myClient:mySecret -X POST https://cas.example.org/cas/oauth2.0/introspect // after curl -u myClient:mySecret -X POST https://cas.example.org/cas/oauth2.0/introspect -d 'token=AT-xxxx'
Defensive patterns
Strategy: validation
Validate before calling
function validateIntrospectionParams(params) {
if (!params.token && !params.access_token) {
throw new Error('introspection requires a token or access_token parameter');
}
const body = new URLSearchParams(params);
return body;
} Type guard
function hasToken(params) {
return params instanceof URLSearchParams && (params.has('token') || params.has('access_token'));
} Try / catch
try {
await introspect(params);
} catch (e) {
if (e.response?.status === 400 && e.response.data?.error === 'missing_access_token') {
// add token parameter and retry once
}
} Prevention
- Always pass the token as a form parameter named 'token'
- Do not send the token in the Authorization: Bearer header for introspection
- Use URLSearchParams / form encoding, not JSON body
- Check parameter names against RFC 7662
When it happens
Trigger: POSTing to /oauth2.0/introspect with valid client credentials but no 'token' (or 'access_token') form parameter, or sending the token in a header/JSON body where CAS does not look for it.
Common situations: Client SDK sends token under a custom field name; token put in the Authorization: Bearer header instead of a form parameter; truncation of the request body; developer assuming GET-style query params are checked when doing a POST without form encoding.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- invalid_request
- invalid_client
- Failed to acquire access token
- Invalid client credentials provided for registered service:
- Code verification method is unrecognized:
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/d1256843bded1547.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oauth-core-api/src/main/java/org/apereo/cas/support/oauth/web/endpoints/OAuth20IntrospectionEndpointController.java:168
return null;
}
protected Optional<Credentials> extractCredentials(final JEEContext context) {
val authExtractor = new BasicAuthExtractor();
val callContext = new CallContext(context, getConfigurationContext().getSessionStore(),
getConfigurationContext().getOauthConfig().getProfileManagerFactory());
return authExtractor.extract(callContext);
}
private Optional<ResponseEntity<? extends @NonNull BaseOAuth20IntrospectionAccessTokenResponse>> validateIntrospectionRequest(
final OAuthRegisteredService registeredService, final UsernamePasswordCredentials credentials,
final HttpServletRequest request) throws Throwable {
val tokenExists = HttpRequestUtils.doesParameterExist(request, OAuth20Constants.TOKEN)
|| HttpRequestUtils.doesParameterExist(request, OAuth20Constants.ACCESS_TOKEN);
if (!tokenExists) {
LOGGER.warn("Access token cannot be found in the request");
return Optional.of(buildBadRequestResponseEntity(OAuth20Constants.MISSING_ACCESS_TOKEN));
}
if (getConfigurationContext().getClientSecretValidator().validate(registeredService, credentials.getPassword())) {
val service = getConfigurationContext().getWebApplicationServiceServiceFactory().createService(registeredService.getServiceId());
val audit = AuditableContext.builder()
.service(service)
.registeredService(registeredService)
.build();
val accessResult = getConfigurationContext().getRegisteredServiceAccessStrategyEnforcer().execute(audit);
return accessResult.isExecutionFailure()
? Optional.of(buildUnauthorizedResponseEntity(OAuth20Constants.UNAUTHORIZED_CLIENT, false))
: Optional.empty();
}
LOGGER.warn("Unable to match client secret for registered service [{}] with client id [{}]",
registeredService.getName(), registeredService.getClientId());
return Optional.of(buildUnauthorizedResponseEntity(OAuth20Constants.INVALID_CLIENT, true));
}View on GitHub (pinned to e7288fc434)