apereo/cas · error · UnauthorizedProxyingException

UNAUTHORIZED_SERVICE_PROXY

UNAUTHORIZED_SERVICE_PROXY

Error message

Proxying is not allowed for registered service 

What it means

When a TicketGrantingTicket was obtained by another service (proxiedBy), AbstractCentralAuthenticationService.evaluateProxiedServiceIfNeeded checks that proxying service's ProxyPolicy. If the policy disallows proxying, it throws UnauthorizedProxyingException (code UNAUTHORIZED_SERVICE_PROXY) naming the registered service id.

Solutions

  1. Update the registered service to allow proxying: set a proxy policy, e.g. new RegexMatchingRegisteredServiceProxyPolicy("^https://your-service/.*")
  2. Ensure the proxying (callback) URL itself is registered in the service registry
  3. If proxying is not needed, stop requesting PGT/PTs and use plain service tickets
  4. Set service proxyPolicy explicitly in the JSON/YAML service definition instead of relying on defaults

Example fix

// before (default: refuse)
"proxyPolicy": null
// after
{
  "@class": "org.apereo.cas.services.RegexMatchingRegisteredServiceProxyPolicy",
  "pattern": "^https://proxy-consumer\.example\.org/.*"
}
Defensive patterns

Strategy: validation

Validate before calling

RegisteredService svc = servicesManager.findServiceBy(proxiedBy);
boolean mayProxy = svc != null && svc.getProxyPolicy() != null && svc.getProxyPolicy().isAllowedToProxy();
if (!mayProxy) throw new IllegalStateException("Enable proxy policy for " + proxiedBy);

Try / catch

try {
    centralAuthenticationService.grantProxyTicket(...);
} catch (UnauthorizedProxyingException e) {
    logger.error("Proxying refused for service {}: {}", serviceId, e.getMessage());
}

Prevention

When it happens

Trigger: Requesting a proxy ticket or ST with a PGT where the pgtUrl's service (proxiedBy) is registered but its registeredService.getProxyPolicy().isAllowedToProxy() is false (e.g. default RefusePermissionToProxyPolicy).

Common situations: Service registered without proxy-granting policy enabled (org.apereo.cas.services.RegexMatchingRegisteredServiceProxyPolicy not set); service not registered at all; CAS 5+ stricter proxy policy enforcement breaking legacy PGT clients.

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/9ef3b405b78b26ad. Report an issue: GitHub.

Appendix: source

Thrown at core/cas-server-core/src/main/java/org/apereo/cas/AbstractCentralAuthenticationService.java:82

        } catch (final Throwable e) {
            LoggingUtils.error(LOGGER, e);
        }
        throw new UnsatisfiedAuthenticationPolicyException(policy);
    }

    protected void evaluateProxiedServiceIfNeeded(@Nullable final Service service,
                                                  final TicketGrantingTicket ticketGrantingTicket,
                                                  @Nullable final RegisteredService registeredService) {
        val proxiedBy = ticketGrantingTicket.getProxiedBy();
        if (proxiedBy != null) {
            LOGGER.debug("Ticket-granting ticket is proxied by [{}]. Locating proxy service in registry...", proxiedBy.getId());
            val proxyingService = configurationContext.getServicesManager().findServiceBy(proxiedBy, CasModelRegisteredService.class);
            if (proxyingService != null) {
                LOGGER.debug("Located proxying service [{}] in the service registry", proxyingService);
                if (!proxyingService.getProxyPolicy().isAllowedToProxy()) {
                    LOGGER.warn("Proxying service [{}] is not authorized to fulfill the proxy attempt made by [{}]",
                        proxyingService.getId(), Objects.requireNonNull(service).getId());
                    throw new UnauthorizedProxyingException(UnauthorizedProxyingException.MESSAGE + Objects.requireNonNull(registeredService).getId());
                }
            } else {
                LOGGER.warn("Proxy attempt by service [{}] (registered service [{}]) is not allowed.",
                    Objects.requireNonNull(service).getId(), Objects.requireNonNull(registeredService).getId());
                throw new UnauthorizedProxyingException(UnauthorizedProxyingException.MESSAGE + registeredService.getId());
            }
        } else {
            LOGGER.trace("Ticket-granting ticket is not proxied by another service");
        }
    }

    protected @Nullable Service resolveServiceFromAuthenticationRequest(final Service service) throws Throwable {
        return configurationContext.getAuthenticationServiceSelectionPlan().resolveService(service, Service.class);
    }

    protected boolean isTicketAuthenticityVerified(final String ticketId) {
        try {
            if (configurationContext.getCipherExecutor() != null) {

View on GitHub (pinned to e7288fc434)