apereo/cas · error · MessageDecodingException

Unable to resolve SP ACS URL for AuthnRequest construction

Error message

Unable to resolve SP ACS URL for AuthnRequest construction

What it means

In the IdP-initiated (unsolicited SSO) flow, the shire parameter is the SP's Assertion Consumer Service URL. extractShire derives it from the shire request parameter or from the registered service's responseLocation/location; if the result is blank the profile cannot construct an AuthnRequest and a MessageDecodingException is thrown.

Solutions

  1. Include the shire parameter in the unsolicited SSO URL: ?providerId=...&shire=https://sp.example.org/acs.
  2. Set responseLocation (or location) on the SamlRegisteredService so the ACS URL can be derived.
  3. Fix the providerId so the correct registered service with a configured location is matched.
  4. Verify the service is actually found in the service registry (enabled, valid date range).

Example fix

// before
<a href="/cas/idp/profile/SAML2/Unsolicited/SSO?providerId=sp.example.org">
// after
<a href="/cas/idp/profile/SAML2/Unsolicited/SSO?providerId=sp.example.org&shire=https://sp.example.org/Shibboleth.sso/SAML2/POST">
Defensive patterns

Strategy: validation

Validate before calling

val shire = request.getParameter("shire");
val service = servicesManager.findServiceBy(providerId, SamlRegisteredService.class);
if (StringUtils.isBlank(shire) && (service == null || (StringUtils.isBlank(service.getResponseLocation()) && StringUtils.isBlank(service.getLocation()))))
    throw new IllegalArgumentException("shire parameter or service ACS location required");

Type guard

boolean hasAcsLocation(SamlRegisteredService s) { return s != null && (StringUtils.isNotBlank(s.getLocation()) || StringUtils.isNotBlank(s.getResponseLocation())); }

Try / catch

try {
    return controller.handleUnsolicitedSsoRequest(request, response);
} catch (MessageDecodingException e) {
    redirect("/error?reason=missing-shire&providerId=" + providerId);
}

Prevention

When it happens

Trigger: GET/POST to the IdP-initiated endpoint where the shire parameter is absent and the registered SAML service for providerId has neither responseLocation nor location set (or the service itself could not be resolved to produce a location).

Common situations: SP integration bookmark/links missing the shire query parameter; service definition imported without ACS location; typo in providerId matching a service with no location configured.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/10caddf0e95f613d. 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:163

        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)