apereo/cas · error

Unable to resolve service provider assertion consumer…

Error message

Unable to resolve service provider assertion consumer service URL for AuthnRequest construction for entityID: [{}]

What it means

During unsolicited (IdP-initiated) SSO, CAS resolved the SP's metadata but could not determine the Assertion Consumer Service (ACS) URL ('shire') to build the AuthnRequest. The chosen AssertionConsumerService had neither a usable ResponseLocation nor Location, or no ACS was present at all, so MessageDecodingException is thrown. CAS refuses to guess an ACS endpoint.

Solutions

  1. Add or correct the AssertionConsumerService (with Binding and Location, optionally ResponseLocation) in the SP's metadata so CAS can resolve an ACS URL.
  2. If the request supplies an ACS index, ensure the SP metadata contains an AssertionConsumerService at that index with the right binding.
  3. Check CAS SamlRegisteredService settings (e.g. assertionConsumerService URLs / white/black lists) are not filtering out the SP's ACS.
  4. Verify the providerId/entityID parameter resolves to the intended EntityDescriptor — a wrong match may select an entity without an ACS.
  5. Inspect DEBUG logs for the metadata adaptor's resolved EntityDescriptor to confirm what ACS entries were seen.

Example fix

// before: SP metadata without ACS
<md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="..."/>
// after: declare the ACS
<md:SPSSODescriptor ... protocolSupportEnumeration="...">
  <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
      Location="https://sp.example.com/Shibboleth.sso/SAML2/POST" index="0" isDefault="true"/>
</md:SPSSODescriptor>
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check SP metadata exposes at least one ACS with a Location
val entity = metadataResolver.resolveSingle(criteria(issuer));
boolean acsOk = entity.getSPSSODescriptor(SAML_20_NS).getAssertionConsumerServices().stream()
    .anyMatch(acs -> StringUtils.isNotBlank(acs.getLocation()));
if (!acsOk) throw new IllegalStateException("SP metadata has no usable ACS for " + issuer);

Type guard

boolean hasAcsLocation(EntityDescriptor ed) { return Optional.ofNullable(ed)
  .map(e -> e.getSPSSODescriptor(SAML20P_NS))
  .map(sp -> sp.getAssertionConsumerServices())
  .map(list -> list.stream().anyMatch(a -> StringUtils.isNotBlank(a.getLocation())))
  .orElse(false); }

Try / catch

try {
    shire = extractShire(request, providerId);
} catch (MessageDecodingException e) {
    LOGGER.error("No ACS resolvable for entityID {}", providerId, e);
    // return a clear error to the requesting application
}

Prevention

When it happens

Trigger: extractShire looks up the SP's AssertionConsumerService for the given providerId/entityID; the optional chain yields blank/null when metadata has no matching AssertionConsumerService entry, or the entry's location and responseLocation are both empty/missing.

Common situations: SP metadata lacks an AssertionConsumerService element (common with hand-written or minimal metadata); IdP-initiated request specifies an ACS index/binding not present in SP metadata; entityID resolves to the wrong EntityDescriptor; white/black-listed ACS filtering in CAS service config excludes the SP's declared ACS.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/30d8662414491faf. 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/SamlIdPInitiatedProfileHandlerController.java:162

        val builder = (SAMLObjectBuilder) builderFactory.getBuilder(qname);
        return clazz.cast(Objects.requireNonNull(builder).buildObject());
    }

    protected String extractShire(final HttpServletRequest request, final String providerId,
                                  final SamlRegisteredServiceMetadataAdaptor facade)
        throws MessageDecodingException {
        var shire = request.getParameter(SamlIdPConstants.SHIRE);
        if (StringUtils.isBlank(shire)) {
            LOGGER.info("Resolving service provider assertion consumer service URL for [{}] and binding [{}]",
                providerId, SAMLConstants.SAML2_POST_BINDING_URI);
            val acs = facade.getAssertionConsumerService(SAMLConstants.SAML2_POST_BINDING_URI);
            shire = Optional.ofNullable(acs)
                .map(service -> StringUtils.isBlank(service.getResponseLocation())
                    ? service.getLocation()
                    : service.getResponseLocation()).orElse(null);
        }
        if (StringUtils.isBlank(shire)) {
            LOGGER.warn("Unable to resolve service provider assertion consumer service URL for AuthnRequest construction for entityID: [{}]", providerId);
            throw new MessageDecodingException("Unable to resolve SP ACS URL for AuthnRequest construction");
        }
        return shire;
    }

    protected String extractProviderId(final HttpServletRequest request) throws MessageDecodingException {
        val providerId = request.getParameter(SamlIdPConstants.PROVIDER_ID);
        if (StringUtils.isBlank(providerId)) {
            LOGGER.warn("No providerId parameter given in unsolicited SSO authentication request.");
            throw new MessageDecodingException("Missing providerId");
        }
        return providerId;
    }
}

View on GitHub (pinned to e7288fc434)