apereo/cas · error · SAMLException
Logout request is not signed but should be for service
Error message
Logout request is not signed but should be for service %s
What it means
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.
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
Example fix
// before (CAS service registry JSON) "@class": "...SamlRegisteredService", "signLogoutRequest": "TRUE" // after: allow unsigned logout for this SP "@class": "...SamlRegisteredService", "signLogoutRequest": "FALSE"
Defensive patterns
Strategy: validation
Validate before calling
// SP side: confirm the logout message is signed before dispatching to the IdP SLO endpoint
if (!logoutMessage.isSigned() && idpRequiresSignedLogout) {
Signer.signObject(logoutMessage); // or configure the SP signing key for SLO bindings
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Unable to find supported NameID format for service
- Signing credentials for validation could not be resolved
- Signing credentials for validation could not be resolved…
- Resource [ ] cannot be located
- Skipped metadata SignatureValidationFilter since signature…
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/c34e2ab0d64bf7d7.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-saml-idp-web/src/main/java/org/apereo/cas/support/saml/web/idp/profile/slo/AbstractSamlSLOProfileHandlerController.java:135
.filter(processor -> processor.supports(request, response, logoutRequest, messageContext))
.forEach(Unchecked.consumer(processor -> processor.receive(request, response, logoutRequest, messageContext)));
val requestDispatcher = request.getServletContext().getRequestDispatcher(CasProtocolConstants.ENDPOINT_LOGOUT);
requestDispatcher.forward(request, response);
}
protected void ensureLogoutRequestIsSignedIfNecessary(final SamlRegisteredService registeredService,
final MessageContext messageContext) throws SAMLException {
var ensureSignature = false;
if (registeredService.getSignLogoutRequest().isUndefined()) {
val logout = configurationContext.getCasProperties().getAuthn().getSamlIdp().getLogout();
ensureSignature = logout.isForceSignedLogoutRequests();
} else {
ensureSignature = registeredService.getSignLogoutRequest().isTrue();
}
if (ensureSignature && !SAMLBindingSupport.isMessageSigned(messageContext)) {
throw new SAMLException("Logout request is not signed but should be for service %s"
.formatted(registeredService.getServiceId()));
}
}
protected <T> T buildSamlObject(final QName qname, final Class<T> clazz) {
val builderFactory = getConfigurationContext().getOpenSamlConfigBean().getBuilderFactory();
val builder = (SAMLObjectBuilder) builderFactory.getBuilder(qname);
return clazz.cast(Objects.requireNonNull(builder).buildObject());
}
protected void handleSloProfileRequest(final HttpServletResponse response,
final HttpServletRequest request,
final BaseHttpServletRequestXMLMessageDecoder decoder,
final String logoutRequestBinding) throws Throwable {
val logout = getConfigurationContext().getCasProperties().getAuthn().getSamlIdp().getLogout();
if (logout.isSingleLogoutCallbacksDisabled()) {
LOGGER.info("Processing SAML2 IdP SLO requests is disabled");
return;View on GitHub (pinned to e7288fc434)