{"record":{"id":"4dddb0425e7f309d","repo":"apereo/cas","slug":"logout-request-is-not-issued-by-a-trusted-issuer","errorCode":null,"errorMessage":"Logout request is not issued by a trusted issuer: [{}]","messagePattern":"Logout request is not issued by a trusted issuer: \\[(.+?)\\]","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/web/controllers/logout/OidcLogoutEndpointController.java","lineNumber":171,"sourceCode":"\n        val registeredService = StringUtils.isNotBlank(givenClientId)\n            ? OAuth20Utils.getRegisteredOAuthServiceByClientId(getConfigurationContext().getServicesManager(), givenClientId, OidcRegisteredService.class)\n            : null;\n\n        if (!enforceIssuer(request, response, registeredService)) {\n            throw new ResponseStatusException(HttpStatus.BAD_REQUEST,\n                configurationContext.getMessageSource().getMessage(\"screen.oidc.issuer.invalid\", ArrayUtils.EMPTY_OBJECT_ARRAY, request.getLocale()));\n        }\n        return executeLogoutRedirect(Optional.ofNullable(StringUtils.trimToNull(state)),\n            Optional.empty(), Optional.ofNullable(givenClientId), request, response);\n    }\n\n    private boolean enforceIssuer(final HttpServletRequest request, final HttpServletResponse response,\n                                  final OidcRegisteredService registeredService) {\n        val webContext = new JEEContext(request, response);\n        if (!getConfigurationContext().getIssuerService().validateIssuer(webContext, List.of(OidcConstants.LOGOUT_URL, \"logout\"), registeredService)) {\n            val issuer = getConfigurationContext().getIssuerService().determineIssuer(Optional.ofNullable(registeredService));\n            LOGGER.warn(\"Logout request is not issued by a trusted issuer: [{}]\", issuer);\n            return false;\n        }\n        return true;\n    }\n\n    protected ResponseEntity executeLogoutRedirect(final Optional<String> state,\n                                                   final Optional<String> redirectUrl,\n                                                   final Optional<String> clientId,\n                                                   final HttpServletRequest request,\n                                                   final HttpServletResponse response) throws Exception {\n        redirectUrl.ifPresent(url -> {\n            val builder = UriComponentsBuilder.fromUriString(url);\n            state.ifPresent(st -> builder.queryParam(OAuth20Constants.STATE, st));\n            clientId.ifPresent(id -> builder.queryParam(OAuth20Constants.CLIENT_ID, id));\n            val logoutUrl = builder.build().toUriString();\n            LOGGER.debug(\"Final logout redirect URL is [{}]\", logoutUrl);\n            WebUtils.putLogoutRedirectUrl(request, logoutUrl);\n        });","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/web/controllers/logout/OidcLogoutEndpointController.java#L153-L189","documentation":"The OIDC logout controller enforces that the logout request's issuer context matches a trusted issuer via the OidcIssuerService (validated against logout URLs and the registered service). If validation fails, enforceIssuer returns false and the logout is refused; the warning records the issuer CAS determined from the registered service.","triggerScenarios":"A logout request reaching OidcLogoutEndpointController whose request host/URL fails validateIssuer for LOGOUT_URL, for the resolved OidcRegisteredService.","commonSituations":"RP post-logout redirect hitting CAS via a different host/scheme than the configured issuer; reverse proxy rewriting URLs; per-service issuer (service-level oidc issuer overrides) mismatched with the actual logout redirect URI; logout URL path not among validated endpoints.","solutions":["Make the RP's end_session_endpoint / logout redirect use exactly the configured CAS OIDC issuer host","Fix proxy header forwarding (Host, X-Forwarded-Proto/Host) so validateIssuer sees the original URL","Review the registered service's issuer/logout configuration so determineIssuer returns the expected value","Update the service's logout URL / serviceId patterns to match the incoming request"],"exampleFix":"// before\npost_logout_redirect via http://localhost:8443/cas/oidc/oidcLogout   # issuer is https://sso.example.org/cas/oidc\n// after\npost_logout_redirect via https://sso.example.org/cas/oidc/oidcLogout","handlingStrategy":"validation","validationCode":"const logoutUrl = new URL(endSessionEndpoint);\nconst issuer = new URL(casAuthnOidcIssuer);\nif (logoutUrl.origin !== issuer.origin) {\n  throw new Error('Logout must be sent to the configured OIDC issuer host');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use the issuer-advertised end_session_endpoint verbatim","Keep proxy/Host headers intact","Keep per-service issuer overrides consistent with the redirect host","Test RP-initiated logout after any hostname or proxy change"],"tags":["oidc","logout","issuer","trust"],"backgroundTag":"invalid-config-value","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"}