{"record":{"id":"629538f7ff905069","repo":"apereo/cas","slug":"service-attempted-to-proxy-but-is-not-allowe","errorCode":null,"errorMessage":"Service [{}] attempted to proxy, but is not allowed.","messagePattern":"Service \\[(.+?)\\] attempted to proxy, but is not allowed\\.","errorType":"exception","errorClass":"UnauthorizedProxyingException","httpStatus":null,"severity":"error","filePath":"core/cas-server-core/src/main/java/org/apereo/cas/DefaultCentralAuthenticationService.java","lineNumber":323,"sourceCode":"\n        val serviceTicket = configurationContext.getTicketRegistry().getTicket(serviceTicketId, ServiceTicket.class);\n        if (serviceTicket == null || serviceTicket.isExpired()) {\n            LOGGER.debug(\"ServiceTicket [{}] has expired or cannot be found in the ticket registry\", serviceTicketId);\n            throw new InvalidTicketException(serviceTicketId);\n        }\n        val registeredService = (CasModelRegisteredService) configurationContext.getServicesManager()\n            .findServiceBy(serviceTicket.getService());\n\n        val ctx = AuditableContext.builder()\n            .serviceTicket(serviceTicket)\n            .authenticationResult(authenticationResult)\n            .registeredService(registeredService)\n            .build();\n\n        enforceRegisteredServiceAccess(ctx);\n\n        if (!Objects.requireNonNull(registeredService).getProxyPolicy().isAllowedToProxy()) {\n            LOGGER.warn(\"Service [{}] attempted to proxy, but is not allowed.\", serviceTicket.getService().getId());\n            throw new UnauthorizedProxyingException();\n        }\n\n        return configurationContext.getLockRepository().execute(serviceTicket.getId(),\n                Unchecked.supplier(() -> {\n                    val authentication = authenticationResult.getAuthentication();\n                    val factory = (ProxyGrantingTicketFactory) configurationContext.getTicketFactory().get(ProxyGrantingTicket.class);\n                    val proxyGrantingTicket = factory.create(serviceTicket, authentication);\n                    val addedTicket = Objects.requireNonNull(configurationContext.getTicketRegistry().addTicket(proxyGrantingTicket));\n                    LOGGER.debug(\"Generated proxy granting ticket [{}] based off of [{}]\", proxyGrantingTicket, serviceTicketId);\n                    if (!serviceTicket.isStateless()) {\n                        configurationContext.getTicketRegistry()\n                            .updateTicket(Objects.requireNonNull(serviceTicket.getTicketGrantingTicket()));\n                    }\n                    val clientInfo = ClientInfoHolder.getClientInfo();\n                    doPublishEvent(new CasProxyGrantingTicketCreatedEvent(this, addedTicket, clientInfo));\n                    return addedTicket;\n                }))","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core/src/main/java/org/apereo/cas/DefaultCentralAuthenticationService.java#L305-L341","documentation":"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.').","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\n\"proxyPolicy\": { \"@class\": \"org.apereo.cas.services.RefuseRegisteredServiceProxyPolicy\" }\n// after\n\"proxyPolicy\": {\n  \"@class\": \"org.apereo.cas.services.RegexMatchingRegisteredServiceProxyPolicy\",\n  \"pattern\": \"^https://myapp.example.org/cas-proxy-callback\"\n}","handlingStrategy":"try-catch","validationCode":"// Check the registered service allows proxying before requesting a PGT\nRegisteredService rs = servicesManager.findServiceBy(service);\nif (rs == null || rs.getProxyPolicy() == null || !rs.getProxyPolicy().isAllowedToProxy()) {\n    throw new IllegalStateException(\"Proxying not enabled for \" + service.getId());\n}","typeGuard":"// Narrow to a proxy-capable policy\nif (rs != null && rs.getProxyPolicy() != null\n        && rs.getProxyPolicy().isAllowedToProxy()) {\n    // safe to call grantProxyGrantingTicket\n}","tryCatchPattern":"try {\n    pgt = cas.grantProxyGrantingTicket(tgtId, stId, creds);\n} catch (UnauthorizedProxyingException e) {\n    logger.error(\"Proxy policy denied: {}\", e.getMessage());\n    // degrade to plain ST validation\n}","preventionTips":["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"],"tags":["proxy-authentication","pgt","service-registry"],"backgroundTag":"insufficient-permissions","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}