apereo/cas · error · SamlException

Assertion consumer service

Error message

Assertion consumer service [%s] cannot be located in metadata [%s]

What it means

When an AuthnRequest supplies an AssertionConsumerServiceURL (or index), SamlIdPUtils.determineEndpointForRequest() verifies it against the SP metadata via adaptor.getAssertionConsumerServiceFor(). If no metadata ACS matches the requested URL/index, CAS refuses to honor the request-supplied ACS (an anti-replay/SSRF safety measure) and throws a formatted SamlException naming the URL and known metadata locations.

Solutions

  1. Compare the ACS URL in the message log with the <md:AssertionConsumerService Location=...> values in the SP metadata and fix whichever is wrong (usually update SP config or metadata).
  2. Refresh/invalidate CAS's cached SAML metadata for the service so newly updated SP ACS entries are picked up.
  3. If the SP should use a metadata-declared ACS, have it omit AssertionConsumerServiceURL/Index from the AuthnRequest.
  4. Check for exact-match issues (scheme, port, trailing slash) between the requested URL and metadata Location.

Example fix

// before (SP request)
<samlp:AuthnRequest AssertionConsumerServiceURL="https://sp.example.com/acs-new" .../>
// after (update SP to the registered ACS)
<samlp:AuthnRequest AssertionConsumerServiceURL="https://sp.example.com/acs" .../>  // matches metadata Location
Defensive patterns

Strategy: try-catch

Validate before calling

// before accepting request ACS
String requestedUrl = authnRequest.getAssertionConsumerServiceURL();
boolean known = requestedUrl == null || adaptor.getAssertionConsumerServices().stream()
    .anyMatch(acs -> requestedUrl.equals(acs.getLocation()));
if (!known) logger.warn("ACS {} not in metadata for {}", requestedUrl, adaptor.getEntityId());

Try / catch

try {
    endpoint = SamlIdPUtils.determineEndpointForRequest(authnRequest, adaptor, binding, fromReq, fromMeta, ctx);
} catch (SamlException e) {
    audit.recordUnrecognizedAcs(authnRequest); // reject rather than retry
    throw e;
}

Prevention

When it happens

Trigger: AuthnRequest carries AssertionConsumerServiceURL or AssertionConsumerServiceIndex; lookup across configured bindings via getAssertionConsumerServiceFor returns empty, so buildAssertionConsumerService is never reached and the String.format SamlException is thrown.

Common situations: SP changed its ACS URL but CAS metadata cache still holds old metadata; SP sends an index not present in metadata; trailing-slash/port mismatches between the request URL and metadata Location; multi-entity metadata where the wrong entity was resolved.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/d95ffdafd047b4b8. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-saml-idp-core/src/main/java/org/apereo/cas/support/saml/SamlIdPUtils.java:160

                    ? adaptor.getAssertionConsumerServiceLocations(binding)
                    : adaptor.getAssertionConsumerServiceLocations();
                val acsUrl = StringUtils.defaultIfBlank(acsFromRequest.getResponseLocation(), acsFromRequest.getLocation());
                val acsIndex = authnRequest instanceof final AuthnRequest authRequest
                    ? authRequest.getAssertionConsumerServiceIndex()
                    : null;

                if (StringUtils.isNotBlank(acsUrl) && locations.stream().anyMatch(acsUrl::equalsIgnoreCase)) {
                    return buildAssertionConsumerService(binding, acsUrl, acsIndex);
                }

                if (acsIndex != null) {
                    val result = adaptor.getAssertionConsumerServiceFor(binding, acsIndex);
                    if (result.isPresent()) {
                        return buildAssertionConsumerService(binding, result.get(), acsIndex);
                    }
                }
                val message = String.format("Assertion consumer service [%s] cannot be located in metadata [%s]", acsUrl, locations);
                throw new SamlException(message);
            }
            return acsFromRequest;
        }
        return acsFromMetadata;
    }

    private static AssertionConsumerService buildAssertionConsumerService(final String binding,
                                                                          final String acsUrl,
                                                                          @Nullable final Integer acsIndex) {
        val acs = new AssertionConsumerServiceBuilder().buildObject();
        acs.setBinding(binding);
        acs.setLocation(acsUrl);
        acs.setResponseLocation(acsUrl);
        acs.setIndex(acsIndex);
        acs.setIsDefault(Boolean.TRUE);
        return acs;
    }

View on GitHub (pinned to e7288fc434)