apereo/cas · error · SAMLException
Request is not signed but should be
Error message
Request is not signed but should be
What it means
During SP-initiated AuthnRequest validation the IdP checks whether the request message is signed. If the IdP metadata says AuthnRequests must be signed and the SP has not opted out of signature validation, an unsigned request is rejected with SAMLException.
Solutions
- Configure the SP to sign its AuthnRequests (sign redirect binding with sigAlg, or use POST binding signed).
- Set cas.authn.saml.idp.webflow... skip validation for the service: registeredService.setSkipValidatingAuthnRequest(true) if policy allows unsigned requests.
- Correct the SP metadata so isAuthnRequestsSigned reflects the SP's actual behavior.
- Verify the signature arrived but failed binding-level detection — check the SigAlg/Signature query parameters are intact (no proxy stripping).
Example fix
// before service.setSkipValidatingAuthnRequest(false); // SP sends unsigned requests // after service.setSkipValidatingAuthnRequest(true); // or configure SP to sign AuthnRequests
Defensive patterns
Strategy: try-catch
Validate before calling
val signed = SAMLBindingSupport.isMessageSigningContext(ctx) || request.getParameter("Signature") != null;
val mustSign = adaptor.isAuthnRequestsSigned() && !registeredService.isSkipValidatingAuthnRequest();
if (mustSign && !signed) LOGGER.warn("Service [{}] will reject this unsigned AuthnRequest", registeredService.getName()); Type guard
boolean willPassSignatureCheck(ctx, adaptor, service) {
return SAMLBindingSupport.isMessageSigned(ctx) || !adaptor.isAuthnRequestsSigned() || service.isSkipValidatingAuthnRequest();
} Try / catch
try {
response = idpController.handleRequest(request, response);
} catch (SAMLException e) {
showSpConfigurationErrorPage("Sign your AuthnRequests or ask the IdP admin to relax the policy");
} Prevention
- Align the authnRequestsSigned flag in SP metadata with the SP's actual signing behavior.
- Enable request signing on the SP (Shibboleth SP: signedRedirect/POST profile settings) when required.
- Only set skipValidatingAuthnRequest=true as a deliberate, documented policy exception.
- Test SP-initiated SSO after any SP metadata change.
When it happens
Trigger: verifyAuthenticationContextSignature sees SAMLBindingSupport.isMessageSigned(ctx)==false while adaptor.isAuthnRequestsSigned() is true and registeredService.isSkipValidatingAuthnRequest() is false — i.e. an unsigned AuthnRequest (e.g. via redirect binding without sigAlg) arrives for a service requiring signed requests.
Common situations: SP sends unsigned redirect-binding AuthnRequests while IdP metadata flag WantAuthnRequestsSigned/authnRequestsSigned is enabled; SP never signs requests; service registered with wrong metadata lacking the signed-requests flag.
Related errors
- Assertion consumer service
- Unable to encrypt assertion for
- Unable to resolve the encryption [public] key for entity id
- Skipped metadata SignatureValidationFilter since signature…
- Required NameID format
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/b533eeb44c288d3b.
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/AbstractSamlIdPProfileHandlerController.java:440
protected void verifyAuthenticationContextSignature(final Pair<? extends SignableSAMLObject, MessageContext> authenticationContext,
final HttpServletRequest request, final RequestAbstractType authnRequest,
final SamlRegisteredServiceMetadataAdaptor adaptor,
final SamlRegisteredService registeredService) throws Throwable {
val ctx = authenticationContext.getValue();
verifyAuthenticationContextSignature(ctx, request, authnRequest, adaptor, registeredService);
}
protected void verifyAuthenticationContextSignature(final MessageContext ctx,
final HttpServletRequest request,
final RequestAbstractType authnRequest,
final SamlRegisteredServiceMetadataAdaptor adaptor,
final SamlRegisteredService registeredService) throws Throwable {
if (!SAMLBindingSupport.isMessageSigned(ctx)) {
LOGGER.trace("The authentication context is not signed");
if (adaptor.isAuthnRequestsSigned() && !registeredService.isSkipValidatingAuthnRequest()) {
LOGGER.error("Metadata for [{}] says authentication requests are signed, yet request is not", adaptor.getEntityId());
throw new SAMLException("Request is not signed but should be");
}
LOGGER.trace("Request is not signed or validation is skipped, so there is no need to verify its signature.");
} else if (adaptor.isAuthnRequestsSigned() && !registeredService.isSkipValidatingAuthnRequest()) {
LOGGER.trace("The authentication context is signed; Proceeding to validate signatures...");
configurationContext.getSamlObjectSignatureValidator().verifySamlProfileRequest(authnRequest, adaptor, request, ctx);
}
}
protected Pair<SamlRegisteredService, SamlRegisteredServiceMetadataAdaptor> getRegisteredServiceAndFacade(
final AuthnRequest authnRequest, final HttpServletRequest httpServletRequest) {
val issuer = SamlIdPUtils.getIssuerFromSamlObject(authnRequest);
LOGGER.debug("Located issuer [{}] from authentication context", issuer);
val registeredService = verifySamlRegisteredService(issuer, httpServletRequest);
LOGGER.debug("Located SAML metadata for [{}]", registeredService.getServiceId());
val adaptor = getSamlMetadataFacadeFor(registeredService, authnRequest);
View on GitHub (pinned to e7288fc434)