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

  1. Register the service in the service registry (JSON file, etc.) so findServiceBy resolves it.
  2. Fix the service definition's accessStrategy: set enabled=true, remove expiration, or adjust required attributes.
  3. Verify the exact service URL (scheme, host, port, path) matches the registered pattern including case sensitivity.
  4. If SSO should be disallowed but login allowed, configure unauthorizedSsoAuthenticationHandler behavior per policy instead of failing resolution.
  5. 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

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


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)