apereo/cas · error
Access token request cannot be validated for grant type
Error message
Access token request cannot be validated for grant type [{}] and client id [{}] given the redirect URI [{}] What it means
In validateInternal of the authorization-code token request validator, the request only validates when redirect_uri and code are present AND the redirect_uri passes checkCallbackValid for the registered client. If any of those checks fails (or no registered service was resolvable), the validator logs this message and returns false, so the token endpoint refuses the authorization_code grant. It is a server-side warning, not a client-facing error string.
Solutions
- Send the exact same redirect_uri in the token request that was used in the /authorize request, and include the code parameter.
- Verify the redirect_uri survives your proxy (configure X-Forwarded-* / correct scheme) so checkCallbackValid still matches.
- Confirm client Basic auth credentials map to the same registered service that owns the code (registeredService.equals(codeRegisteredService)).
- Enable CAS debug logging for org.apereo.cas.support.oauth to see which of redirectUri/code/callback checks failed.
Example fix
// before POST /oauth2.0/token grant_type=authorization_code&code=ABC // after POST /oauth2.0/token grant_type=authorization_code&code=ABC&redirect_uri=https://app.example.com/cb
Defensive patterns
Strategy: validation
Validate before calling
if (!code || !redirectUri) {
throw new Error('token exchange requires both code and redirect_uri');
}
if (redirectUri !== authorizeRedirectUri) {
throw new Error('redirect_uri must match the one used in /authorize');
} Try / catch
try {
const token = await exchangeCodeForToken({ code, redirectUri });
} catch (e) {
if (/invalid_grant|cannot be validated/.test(e.message)) {
// re-check redirect_uri parity and code expiry, then restart the flow
}
throw e;
} Prevention
- Store the redirect_uri used at /authorize and reuse it verbatim in the token POST.
- Never run the authorization-code flow behind a proxy that rewrites scheme/host without forwarding headers configured in CAS.
- Handle code expiry: codes are single-use and short-lived; restart the flow rather than retrying a stale code.
- Verify Basic auth client_id matches the service that owns the code.
When it happens
Trigger: POST /oauth2.0/token?grant_type=authorization_code where redirect_uri is missing, the code parameter is missing, or redirect_uri does not match the callback registered for the client_id found in the authenticated profile.
Common situations: Client omits redirect_uri on the token exchange though it was sent on /authorize (required for parity); redirect_uri rewritten by a proxy/load balancer (scheme or host changed); code parameter lost due to form-encoding issues in the token POST; client_id attribute missing from the basic-auth profile so the wrong service resolves.
Related errors
- Response type not authorized for service
- Subject token type is not supported
- Actor token type is not supported
- Cannot save a resource set with inconsistent scopes.
- Cannot update a resource set without identifiers.
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/a5c8c4dccb184209.
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/OAuth20AuthorizationCodeGrantTypeTokenRequestValidator.java:112
.authentication(oauthCode.getAuthentication())
.principal(accessStrategyPrincipal)
.build();
val accessResult = configurationContext.getRegisteredServiceAccessStrategyEnforcer().execute(audit);
accessResult.throwExceptionIfNeeded();
if (!registeredService.equals(codeRegisteredService)) {
LOGGER.warn("OAuth code [{}] issued to service [{}] does not match [{}] provided, given the redirect URI [{}]",
code, serviceId, registeredService.getName(), redirectUri);
return false;
}
if (!isGrantTypeSupportedBy(registeredService, grantType)) {
LOGGER.warn("Requested grant type [{}] is not authorized by service definition [{}]", grantType, registeredService.getServiceId());
return false;
}
return true;
}
LOGGER.warn("Access token request cannot be validated for grant type [{}] and client id [{}] given the redirect URI [{}]", grantType, clientId, redirectUri);
return false;
}
}
View on GitHub (pinned to e7288fc434)