{"record":{"id":"a72c55ce69444147","repo":"apereo/cas","slug":"unauthorized","errorCode":null,"errorMessage":"Unauthorized","messagePattern":"Unauthorized","errorType":"exception","errorClass":"UnauthorizedServiceException","httpStatus":403,"severity":"error","filePath":"core/cas-server-core-services-api/src/main/java/org/apereo/cas/services/RegisteredServiceAccessStrategyAuditableEnforcer.java","lineNumber":212,"sourceCode":"    @Audit(action = AuditableActions.SERVICE_ACCESS_ENFORCEMENT,\n        actionResolverName = AuditActionResolvers.SERVICE_ACCESS_ENFORCEMENT_ACTION_RESOLVER,\n        resourceResolverName = AuditResourceResolvers.SERVICE_ACCESS_ENFORCEMENT_RESOURCE_RESOLVER)\n    public AuditableExecutionResult execute(final AuditableContext context) {\n        return byExternalAccessStrategyEnforcers(context)\n            .or(() -> byServiceTicketAndAuthnResultAndRegisteredService(context))\n            .or(() -> byServiceAndRegisteredServiceAndTicketGrantingTicket(context))\n            .or(() -> byServiceAndRegisteredServiceAndPrincipal(context))\n            .or(() -> byServiceAndRegisteredServiceAndAuthentication(context))\n            .or(() -> byServiceAndRegisteredService(context))\n            .or(() -> byRegisteredService(context))\n            .orElseGet(() -> {\n                val result = AuditableExecutionResult.builder()\n                    .registeredService(context.getRegisteredService().orElse(null))\n                    .service(context.getService().orElse(null))\n                    .authentication(context.getAuthentication().orElse(null))\n                    .build();\n                result.setException(UnauthorizedServiceException.denied(\"Unauthorized\"));\n                LOGGER.warn(\"Service is not registered in the service registry. \"\n                        + \"Service is [{}] and registered service is [{}]\",\n                    result.getService().map(Service::getId).orElse(null),\n                    result.getRegisteredService().map(RegisteredService::getName).orElse(null));\n                return result;\n            });\n    }\n\n    protected Optional<AuditableExecutionResult> byExternalAccessStrategyEnforcers(final AuditableContext context) {\n        val enforcers = applicationContext.getBeansOfType(RegisteredServiceAccessStrategyEnforcer.class).values();\n        return enforcers\n            .stream()\n            .filter(BeanSupplier::isNotProxy)\n            .sorted(AnnotationAwareOrderComparator.INSTANCE)\n            .map(Unchecked.function(enforcer -> enforcer.execute(context)))\n            .filter(Objects::nonNull)\n            .filter(AuditableExecutionResult::isExecutionFailure)\n            .findFirst();\n    }","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-services-api/src/main/java/org/apereo/cas/services/RegisteredServiceAccessStrategyAuditableEnforcer.java#L194-L230","documentation":"RegisteredServiceAccessStrategyAuditableEnforcer evaluates whether an incoming service request is authorized. When the requested service has no matching registered service in the service registry, the auditable result is marked with UnauthorizedServiceException('Unauthorized'). CAS refuses to issue tickets for unregistered services as a core security policy.","triggerScenarios":"execute() runs on an AuditableContext whose service does not match any RegisteredService in the configured service registry (no service definition whose serviceId pattern matches the requested service URL).","commonSituations":"Fresh CAS deployment with an empty/in-memory registry; service registry source (JSON, YAML, JDBC, Mongo) not loaded or misconfigured path/URL; regex in the service definition does not match the callback URL (trailing slashes, query params, http vs https); registry not reloaded after adding the service.","solutions":["Add/register the service in the service registry (e.g. JSON file in the services directory) with a pattern matching the service URL","Verify the service registry configuration (registry location/connection) and that CAS actually loaded the services (check logs/services admin UI)","Loosen or correct the serviceId regex so it matches the actual callback URL (test with the regex evaluator)","Check the service registry is watchable/reload-enabled or restart CAS after adding definitions"],"exampleFix":"// before: no matching definition\n// {\"@class\":\"org.apereo.cas.services.RegexRegisteredService\",\"serviceId\":\"^https://old.example.com/.*\",...}\n// after: pattern matches actual callback\n// {\"@class\":\"org.apereo.cas.services.RegexRegisteredService\",\"serviceId\":\"^https://app.example.com/(.*)\",\"id\":1,\"name\":\"App\",...}","handlingStrategy":"validation","validationCode":"Optional<RegisteredService> rs = servicesManager.findServiceBy(service);\nif (rs.isEmpty()) {\n    // service will be denied; register it before redirecting clients\n}","typeGuard":"boolean isServiceRegistered(ServicesManager sm, Service s) {\n    return s != null && sm.findServiceBy(s).isPresent();\n}","tryCatchPattern":"try { enforcer.execute(ctx); } catch (UnauthorizedServiceException e) {\n    renderUnauthorizedView(ctx.getService()); // friendly error page\n}","preventionTips":["Add service definitions before pointing client apps at CAS","Use hot-reload/watchable service registry storage","Test serviceId regexes against exact callback URLs (scheme, path, params)","Monitor 'Service is not registered' warnings in logs"],"tags":["unauthorized-service","service-registry","cas","access-control"],"backgroundTag":"resource-not-found","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"}