apereo/cas · error · PrincipalException
Cannot authorize principal
Error message
Cannot authorize principal %s to access service %s, likely due to insufficient permissions
What it means
The registered service's RegisteredServiceAccessStrategy (authorizeRequest) denied the request for this specific principal, so CAS refuses to let the principal access the service. An UnauthorizedServiceForPrincipalException is wrapped in a PrincipalException carrying the handler error, which the authentication/protocol layer turns into an unauthorized-service response.
Solutions
- Inspect the service definition's accessStrategy (authorizedUsers / userAttributes) and confirm the principal id and attributes actually satisfy it.
- Dump the principal's attributes at authentication time and compare against required attributes — fix the attribute repository or release policy if attributes are absent.
- Correct casing: if caseSensitive is true (default), match the authorized users/attributes exactly.
- Update the service registry entry (or remove the restrictive accessStrategy) and reload services.
- Enable debug logging on RegisteredServiceAccessStrategy enforcers to see exactly which rule rejected the request.
Example fix
// before: service JSON denies principal
"accessStrategy": { "@class": "org.apereo.cas.services.DefaultRegisteredServiceAccessStrategy", "serviceAuthorizedUsers": ["hostnmaster"] }
// after: include the actual principal id
"accessStrategy": { "@class": "org.apereo.cas.services.DefaultRegisteredServiceAccessStrategy", "serviceAuthorizedUsers": ["hostnmaster", "alice"] } Defensive patterns
Strategy: try-catch
Validate before calling
RegisteredService svc = servicesManager.findServiceBy(serviceId);
RegisteredServiceAccessStrategy s = svc.getAccessStrategy();
boolean allowed = s.isServiceAccessAllowed(principalId, principalAttributes);
if (!allowed) { // do not attempt access
} Try / catch
try {
boolean ok = enforcer.authorize(context);
} catch (PrincipalException e) {
if (e.getHandlerErrors().containsKey("UnauthorizedServiceForPrincipalException")) {
// render access-denied for this principal on this service
}
} Prevention
- Keep authorizedUsers/required-attribute lists in service definitions in sync with your user directory.
- Remember default case-sensitivity of user/attribute matching.
- Verify attribute release policies deliver the attributes the access strategy requires.
- Review service definitions after copying between environments.
When it happens
Trigger: Calling authorize during access-strategy enforcement when the service's access strategy evaluates false for this principal — e.g. serviceAuthorizedUsers/authorizedUsers does not list the principal, required principal attributes (userAttributes) don't match the principal's actual attributes, or a delegated authorizeRequest rule (caseSensitive, rejects users) fails.
Common situations: Service JSON/registry entry lists the wrong username or the pattern is case-mismatched; required attribute missing from the principal because the attribute repository changed or wasn't released; users moved between LDAP groups; copied a service definition that names another environment's test users.
Understand the failure class
Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.
Related errors
- Denied
- cannot be authorized
- UNAUTHORIZED_SERVICE_PROXY
- Unauthorized account removal attempt
- Unknown authorization header type
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/77992deaa2412dd9.
Report an issue: GitHub.
Appendix: source
Thrown at core/cas-server-core-services-api/src/main/java/org/apereo/cas/services/DefaultRegisteredServicePrincipalAccessStrategyEnforcer.java:40
@Override
public Boolean authorize(final PrincipalAccessStrategyContext context) {
RegisteredServiceAccessStrategyUtils.ensureServiceAccessIsAllowed(context.getService(), context.getRegisteredService());
val serviceId = context.getService() != null ? context.getService().getId() : "unknown";
LOGGER.trace("Checking access strategy for service [{}], requested by [{}] with attributes [{}].", serviceId, context.getPrincipalId(), context.getPrincipalAttributes());
val accessRequest = RegisteredServiceAccessStrategyRequest.builder()
.service(context.getService())
.principalId(context.getPrincipalId())
.attributes(context.getPrincipalAttributes())
.registeredService(context.getRegisteredService())
.applicationContext(this.applicationContext)
.build();
if (Unchecked.supplier(() -> !context.getRegisteredService().getAccessStrategy().authorizeRequest(accessRequest)).get()) {
LOGGER.warn("Cannot grant access to service [{}]; it is not authorized for use by [{}].", serviceId, context.getPrincipalId());
val handlerErrors = new HashMap<String, Throwable>();
val message = String.format("Cannot authorize principal %s to access service %s, likely due to insufficient permissions", context.getPrincipalId(), serviceId);
val exception = new UnauthorizedServiceForPrincipalException(message, context.getRegisteredService(), context.getPrincipalId(), context.getPrincipalAttributes());
handlerErrors.put(UnauthorizedServiceForPrincipalException.class.getSimpleName(), exception);
throw new PrincipalException(message, handlerErrors, new HashMap<>());
}
return true;
}
}
View on GitHub (pinned to e7288fc434)