apereo/cas · error · CredentialsException
Missing scope [ ]. Unable to authenticate access token
Error message
Missing scope [%s]. Unable to authenticate access token %s
What it means
BaseUmaTokenAuthenticator.validate extracts the UMA access token from the request, looks it up in the ticket registry as an OAuth20AccessToken, and requires that the token carries the configured required UMA scope (e.g. uma_protection). If the token's scopes do not include it, a CredentialsException is thrown and the request is not authenticated. This enforces that only protection-scoped tokens may call UMA protection endpoints.
Solutions
- Request the required scope (default 'uma_protection') in the OAuth2 authorization/token request.
- Check which scope BaseUmaTokenAuthenticator is configured with and add it to the client's allowed scopes.
- Issue a new access token after fixing the scope request; existing tokens cannot be amended.
- Verify the service definition's supported scopes/evaluator are not stripping the scope at grant time.
Example fix
// before github-like client: GET /oauth2.0/authorize?client_id=c&response_type=code&scope=read // after GET /oauth2.0/authorize?client_id=c&response_type=code&scope=read%20uma_protection
Defensive patterns
Strategy: try-catch
Validate before calling
OAuth20AccessToken at = ticketRegistry.getTicket(token, OAuth20AccessToken.class);
if (at == null || !at.getScopes().contains("uma_protection")) {
throw new CredentialsException("token lacks uma_protection scope");
} Try / catch
try { authenticator.validate(callContext, creds); } catch (CredentialsException e) {
return Optional.empty(); // results in 401 with WWW-Authenticate
} Prevention
- Always request uma_protection (or your configured required scope) when minting tokens for UMA clients
- Document required scopes in client onboarding
- Verify scopes in the token immediately after issuance in tests
When it happens
Trigger: Presenting an OAuth2 access token to a UMA endpoint whose grant did not request/include the required scope (e.g. token issued without 'uma_protection'), typically at the permission or resource-set registration endpoints.
Common situations: Clients requesting tokens without the uma_protection scope in the authorization request; CAS service definition narrowing the allowed scopes; using a regular API token instead of one minted for UMA protection.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Cannot save a resource set with inconsistent scopes.
- Cannot update a resource set without identifiers.
- Cannot update a resource set with inconsistent/mismatched…
- Cannot save a resource set with inconsistent scopes.
- Authentication handler is disabled
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/9cd1e916bce00270.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oauth-uma-core/src/main/java/org/apereo/cas/uma/web/authn/BaseUmaTokenAuthenticator.java:40
*
* @author Misagh Moayyed
* @since 6.0.0
*/
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
@Slf4j
public abstract class BaseUmaTokenAuthenticator implements Authenticator {
private final TicketRegistry ticketRegistry;
private final JwtBuilder accessTokenJwtBuilder;
@Override
public Optional<Credentials> validate(final CallContext callContext, final Credentials creds) {
val credentials = (TokenCredentials) creds;
val token = extractAccessTokenFrom(credentials.getToken().trim());
val at = ticketRegistry.getTicket(token, OAuth20AccessToken.class);
if (!at.getScopes().contains(getRequiredScope())) {
val err = String.format("Missing scope [%s]. Unable to authenticate access token %s", getRequiredScope(), token);
throw new CredentialsException(err);
}
val profile = new CommonProfile();
val authentication = at.getAuthentication();
val principal = authentication.getPrincipal();
profile.setId(principal.getId());
val attributes = new LinkedHashMap<String, Object>(authentication.getAttributes());
attributes.putAll(principal.getAttributes());
profile.addAttributes(attributes);
profile.addRoles(at.getScopes());
profile.addAttribute(OAuth20AccessToken.class.getName(), at);
profile.addAttribute(OAuth20Constants.CLIENT_ID, at.getClientId());
LOGGER.debug("Authenticated access token [{}]", profile);
credentials.setUserProfile(profile);
return Optional.of(credentials);
}
View on GitHub (pinned to e7288fc434)