apereo/cas · error · UnauthorizedSsoServiceException
Denied
Error message
Denied: %s
What it means
RegisteredServiceAuthenticationHandlerResolver.supports throws UnauthorizedSsoServiceException when the requested service is either not registered in the ServicesManager or its access strategy denies access. This blocks SSO participation for that service before any handler resolution happens. Note the thrown message also carries the deny reason from the access strategy in newer versions.
Solutions
- Register the service in the service registry (JSON file, etc.) so findServiceBy resolves it.
- Fix the service definition's accessStrategy: set enabled=true, remove expiration, or adjust required attributes.
- Verify the exact service URL (scheme, host, port, path) matches the registered pattern including case sensitivity.
- If SSO should be disallowed but login allowed, configure unauthorizedSsoAuthenticationHandler behavior per policy instead of failing resolution.
- Check the WARN log line 'is not allowed to use SSO' to identify which registered service (or null) was matched.
Example fix
// before (JSON service def)
"@class": "org.apereo.cas.services.RegexRegisteredService",
"serviceId": "^https://app.example.org/.*",
"accessStrategy": { "@class": "...DefaultRegisteredServiceAccessStrategy", "enabled": false }
// after
"accessStrategy": { "@class": "...DefaultRegisteredServiceAccessStrategy", "enabled": true, "ssoEnabled": true } Defensive patterns
Strategy: validation
Validate before calling
// before login, verify the service is registered and allowed
var service = authenticationServiceSelectionPlan.resolveService(context.getService());
var reg = servicesManager.findServiceBy(service);
boolean ok = reg != null && reg.getAccessStrategy().isServiceAccessAllowed(reg, service);
if (!ok) { throw new UnauthorizedSsoServiceException("Service not authorized: " + service); } Try / catch
try {
return handlers.resolve(transaction);
} catch (UnauthorizedSsoServiceException e) {
LOGGER.warn("Service denied SSO access: [{}]", e.getMessage());
return buildUnauthorizedServiceErrorView(context);
} Prevention
- Keep service registry entries reviewed: enabled=true, no expired access strategies.
- Match serviceId patterns against the exact URLs clients actually send (scheme/host/port).
- Automate service registry audits so stale/disabled definitions are caught before clients break.
When it happens
Trigger: A login/SSO request reaches handler resolution for a service whose findServiceBy(service) returns null, or whose RegisteredServiceAccessStrategy.isServiceAccessAllowed returns false (service disabled, expired, unauthorized delegation/SSO, or attribute-based rejection).
Common situations: Service not registered at all (no JSON/regex service registry entry); service definition disabled=true; service access strategy with snoozed expiration or enabled=false; case/scheme mismatch causing no registry match; attribute release policy denying the user.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- cannot be authorized
- Cannot authorize principal
- Provided authentication result is undefined to evaluate for…
- screen.service.error.message
- Authentication handler is disabled
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/70eb73817ce5fbfa.
Report an issue: GitHub.
Appendix: source
Thrown at core/cas-server-core-authentication-api/src/main/java/org/apereo/cas/authentication/handler/RegisteredServiceAuthenticationHandlerResolver.java:65
@Override
public Set<AuthenticationHandler> resolve(final Set<AuthenticationHandler> candidateHandlers,
final AuthenticationTransaction transaction) throws Throwable {
val service = authenticationServiceSelectionPlan.resolveService(transaction.getService());
val registeredService = servicesManager.findServiceBy(service);
val requiredHandlers = filterRequiredAuthenticationHandlers(candidateHandlers, service, registeredService, transaction);
return filterExcludedAuthenticationHandlers(requiredHandlers, service, registeredService);
}
@Override
public boolean supports(final Set<AuthenticationHandler> handlers, final AuthenticationTransaction transaction) throws Throwable {
val service = authenticationServiceSelectionPlan.resolveService(transaction.getService());
if (service != null) {
val registeredService = servicesManager.findServiceBy(service);
LOGGER.trace("Located registered service definition [{}] for this authentication transaction", registeredService);
if (registeredService == null || !registeredService.getAccessStrategy().isServiceAccessAllowed(registeredService, service)) {
LOGGER.warn("Service [{}] is not allowed to use SSO.", service);
throw new UnauthorizedSsoServiceException("Denied: %s".formatted(service));
}
val authenticationPolicy = registeredService.getAuthenticationPolicy();
return !authenticationPolicy.getRequiredAuthenticationHandlers().isEmpty()
|| !authenticationPolicy.getExcludedAuthenticationHandlers().isEmpty();
}
return false;
}
protected Set<AuthenticationHandler> filterExcludedAuthenticationHandlers(
final Set<AuthenticationHandler> candidateHandlers,
@Nullable final Service service,
@Nullable final RegisteredService registeredService) {
val authenticationPolicy = Objects.requireNonNull(registeredService).getAuthenticationPolicy();
val excludedHandlers = authenticationPolicy.getExcludedAuthenticationHandlers();
LOGGER.debug("Authentication transaction excludes [{}] for service [{}]", excludedHandlers, service);
val handlerSet = new LinkedHashSet<>(candidateHandlers);View on GitHub (pinned to e7288fc434)