apereo/cas · error · UnauthorizedProxyingException
Service [ ] attempted to proxy, but is not allowed.
Error message
Service [{}] attempted to proxy, but is not allowed. What it means
createProxyGrantingTicket enforces that the registered service owning the presented service ticket has a proxy policy allowing proxying. If registeredService.getProxyPolicy().isAllowedToProxy() is false, it throws UnauthorizedProxyingException (default message 'Service [{}] attempted to proxy, but is not allowed.').
Solutions
- Set an allowing proxyPolicy on the registered service (RegexMatchingRegisteredServiceProxyPolicy matching the proxy callback URL)
- Configure the proxy callback URL (pgtCallbackUrl) for the client and register it in the policy pattern
- Restart/refresh the services manager cache after editing the registry entry
- If proxying is unnecessary, switch the client to plain service-ticket validation
Example fix
// before
"proxyPolicy": { "@class": "org.apereo.cas.services.RefuseRegisteredServiceProxyPolicy" }
// after
"proxyPolicy": {
"@class": "org.apereo.cas.services.RegexMatchingRegisteredServiceProxyPolicy",
"pattern": "^https://myapp.example.org/cas-proxy-callback"
} Defensive patterns
Strategy: try-catch
Validate before calling
// Check the registered service allows proxying before requesting a PGT
RegisteredService rs = servicesManager.findServiceBy(service);
if (rs == null || rs.getProxyPolicy() == null || !rs.getProxyPolicy().isAllowedToProxy()) {
throw new IllegalStateException("Proxying not enabled for " + service.getId());
} Type guard
// Narrow to a proxy-capable policy
if (rs != null && rs.getProxyPolicy() != null
&& rs.getProxyPolicy().isAllowedToProxy()) {
// safe to call grantProxyGrantingTicket
} Try / catch
try {
pgt = cas.grantProxyGrantingTicket(tgtId, stId, creds);
} catch (UnauthorizedProxyingException e) {
logger.error("Proxy policy denied: {}", e.getMessage());
// degrade to plain ST validation
} Prevention
- Register an explicit allowing proxyPolicy for every proxy-capable service
- Configure and verify the proxy callback URL end-to-end
- Re-check registry entries after services-manager edits or cache refreshes
- Automate a smoke test that obtains a PGT in CI
When it happens
Trigger: Calling grantProxyGrantingTicket(pgtTicketId, serviceTicketId, credentials) with a service ticket whose registered service has no proxy policy or a deny-all policy (e.g. RefuseRegisteredServiceProxyPolicy or missing proxyPolicy).
Common situations: Service upgraded to proxy authentication without updating its registry entry; registry entry seeded from a template with proxyPolicy unset (deny by default); admin disabled proxying in the services manager; proxy callback URL config not performed so policy remains default.
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
- cannot be authorized
- The service definition file could not be saved at
- Unauthorized
- Service is not found or is disabled in the service registry.
- Registered service template definition
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/629538f7ff905069.
Report an issue: GitHub.
Appendix: source
Thrown at core/cas-server-core/src/main/java/org/apereo/cas/DefaultCentralAuthenticationService.java:323
val serviceTicket = configurationContext.getTicketRegistry().getTicket(serviceTicketId, ServiceTicket.class);
if (serviceTicket == null || serviceTicket.isExpired()) {
LOGGER.debug("ServiceTicket [{}] has expired or cannot be found in the ticket registry", serviceTicketId);
throw new InvalidTicketException(serviceTicketId);
}
val registeredService = (CasModelRegisteredService) configurationContext.getServicesManager()
.findServiceBy(serviceTicket.getService());
val ctx = AuditableContext.builder()
.serviceTicket(serviceTicket)
.authenticationResult(authenticationResult)
.registeredService(registeredService)
.build();
enforceRegisteredServiceAccess(ctx);
if (!Objects.requireNonNull(registeredService).getProxyPolicy().isAllowedToProxy()) {
LOGGER.warn("Service [{}] attempted to proxy, but is not allowed.", serviceTicket.getService().getId());
throw new UnauthorizedProxyingException();
}
return configurationContext.getLockRepository().execute(serviceTicket.getId(),
Unchecked.supplier(() -> {
val authentication = authenticationResult.getAuthentication();
val factory = (ProxyGrantingTicketFactory) configurationContext.getTicketFactory().get(ProxyGrantingTicket.class);
val proxyGrantingTicket = factory.create(serviceTicket, authentication);
val addedTicket = Objects.requireNonNull(configurationContext.getTicketRegistry().addTicket(proxyGrantingTicket));
LOGGER.debug("Generated proxy granting ticket [{}] based off of [{}]", proxyGrantingTicket, serviceTicketId);
if (!serviceTicket.isStateless()) {
configurationContext.getTicketRegistry()
.updateTicket(Objects.requireNonNull(serviceTicket.getTicketGrantingTicket()));
}
val clientInfo = ClientInfoHolder.getClientInfo();
doPublishEvent(new CasProxyGrantingTicketCreatedEvent(this, addedTicket, clientInfo));
return addedTicket;
}))View on GitHub (pinned to e7288fc434)