{"record":{"id":"c34e2ab0d64bf7d7","repo":"apereo/cas","slug":"logout-request-is-not-signed-but-should-be-for-ser","errorCode":null,"errorMessage":"Logout request is not signed but should be for service %s","messagePattern":"Logout request is not signed but should be for service (.+?)","errorType":"exception","errorClass":"SAMLException","httpStatus":null,"severity":"error","filePath":"support/cas-server-support-saml-idp-web/src/main/java/org/apereo/cas/support/saml/web/idp/profile/slo/AbstractSamlSLOProfileHandlerController.java","lineNumber":135,"sourceCode":"            .filter(processor -> processor.supports(request, response, logoutRequest, messageContext))\n            .forEach(Unchecked.consumer(processor -> processor.receive(request, response, logoutRequest, messageContext)));\n\n        val requestDispatcher = request.getServletContext().getRequestDispatcher(CasProtocolConstants.ENDPOINT_LOGOUT);\n        requestDispatcher.forward(request, response);\n    }\n\n    protected void ensureLogoutRequestIsSignedIfNecessary(final SamlRegisteredService registeredService,\n                                                          final MessageContext messageContext) throws SAMLException {\n        var ensureSignature = false;\n        if (registeredService.getSignLogoutRequest().isUndefined()) {\n            val logout = configurationContext.getCasProperties().getAuthn().getSamlIdp().getLogout();\n            ensureSignature = logout.isForceSignedLogoutRequests();\n        } else {\n            ensureSignature = registeredService.getSignLogoutRequest().isTrue();\n        }\n\n        if (ensureSignature && !SAMLBindingSupport.isMessageSigned(messageContext)) {\n            throw new SAMLException(\"Logout request is not signed but should be for service %s\"\n                .formatted(registeredService.getServiceId()));\n        }\n    }\n\n    protected <T> T buildSamlObject(final QName qname, final Class<T> clazz) {\n        val builderFactory = getConfigurationContext().getOpenSamlConfigBean().getBuilderFactory();\n        val builder = (SAMLObjectBuilder) builderFactory.getBuilder(qname);\n        return clazz.cast(Objects.requireNonNull(builder).buildObject());\n    }\n\n    protected void handleSloProfileRequest(final HttpServletResponse response,\n                                           final HttpServletRequest request,\n                                           final BaseHttpServletRequestXMLMessageDecoder decoder,\n                                           final String logoutRequestBinding) throws Throwable {\n        val logout = getConfigurationContext().getCasProperties().getAuthn().getSamlIdp().getLogout();\n        if (logout.isSingleLogoutCallbacksDisabled()) {\n            LOGGER.info(\"Processing SAML2 IdP SLO requests is disabled\");\n            return;","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-saml-idp-web/src/main/java/org/apereo/cas/support/saml/web/idp/profile/slo/AbstractSamlSLOProfileHandlerController.java#L117-L153","documentation":"The SAML IdP single-logout handler enforces that incoming LogoutRequests are signed when the registered service requires it (service's signLogoutRequest or the global forceSignedLogoutRequests setting). When SAMLBindingSupport.isMessageSigned reports the message is unsigned, the handler rejects it with this SAMLException. This is a policy enforcement on the SP's logout binding.","triggerScenarios":"handleLogoutRequest -> ensureLogoutRequestIsSignedIfNecessary when registeredService.getSignLogoutRequest() is true (or samlIdp logout.forceSignedLogoutRequests=true and service is undefined) and the received LogoutRequest arrived over a binding with no signature (e.g. unsigned redirect binding).","commonSituations":"SP sends unsigned SLO redirect messages while CAS service config mandates signing; operator enabled forceSignedLogoutRequests globally but a legacy SP cannot sign; signLogoutRequest flipped to true in service registry without updating the SP.","solutions":["Configure the SP to sign its LogoutRequests (set its AuthnRequestsSigned/SLO signing on)","Or set signLogoutRequest=false for that SamlRegisteredService in the CAS service registry","Or disable cas.authn.samlIdp.logout.force-signed-logout-requests if policy permits","Verify the SP uses POST binding with signature, or signs redirect-binding messages via SigAlg/Signature query params"],"exampleFix":"// before (CAS service registry JSON)\n\"@class\": \"...SamlRegisteredService\", \"signLogoutRequest\": \"TRUE\"\n// after: allow unsigned logout for this SP\n\"@class\": \"...SamlRegisteredService\", \"signLogoutRequest\": \"FALSE\"","handlingStrategy":"validation","validationCode":"// SP side: confirm the logout message is signed before dispatching to the IdP SLO endpoint\nif (!logoutMessage.isSigned() && idpRequiresSignedLogout) {\n    Signer.signObject(logoutMessage); // or configure the SP signing key for SLO bindings\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Mirror the CAS service registry's signLogoutRequest setting in the SP's SLO signing config","Enable forceSignedLogoutRequests only after confirming all SPs can sign","Document per-service signing requirements in the service registry"],"tags":["saml","single-logout","signature-validation","service-config"],"backgroundTag":"unsigned-saml-message","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"}