{"record":{"id":"10caddf0e95f613d","repo":"apereo/cas","slug":"unable-to-resolve-sp-acs-url-for-authnrequest-cons","errorCode":null,"errorMessage":"Unable to resolve SP ACS URL for AuthnRequest construction","messagePattern":"Unable to resolve SP ACS URL for AuthnRequest construction","errorType":"exception","errorClass":"MessageDecodingException","httpStatus":null,"severity":"error","filePath":"support/cas-server-support-saml-idp-web/src/main/java/org/apereo/cas/support/saml/web/idp/profile/SamlIdPInitiatedProfileHandlerController.java","lineNumber":163,"sourceCode":"        return clazz.cast(Objects.requireNonNull(builder).buildObject());\n    }\n\n    protected String extractShire(final HttpServletRequest request, final String providerId,\n                                  final SamlRegisteredServiceMetadataAdaptor facade)\n        throws MessageDecodingException {\n        var shire = request.getParameter(SamlIdPConstants.SHIRE);\n        if (StringUtils.isBlank(shire)) {\n            LOGGER.info(\"Resolving service provider assertion consumer service URL for [{}] and binding [{}]\",\n                providerId, SAMLConstants.SAML2_POST_BINDING_URI);\n            val acs = facade.getAssertionConsumerService(SAMLConstants.SAML2_POST_BINDING_URI);\n            shire = Optional.ofNullable(acs)\n                .map(service -> StringUtils.isBlank(service.getResponseLocation())\n                    ? service.getLocation()\n                    : service.getResponseLocation()).orElse(null);\n        }\n        if (StringUtils.isBlank(shire)) {\n            LOGGER.warn(\"Unable to resolve service provider assertion consumer service URL for AuthnRequest construction for entityID: [{}]\", providerId);\n            throw new MessageDecodingException(\"Unable to resolve SP ACS URL for AuthnRequest construction\");\n        }\n        return shire;\n    }\n\n    protected String extractProviderId(final HttpServletRequest request) throws MessageDecodingException {\n        val providerId = request.getParameter(SamlIdPConstants.PROVIDER_ID);\n        if (StringUtils.isBlank(providerId)) {\n            LOGGER.warn(\"No providerId parameter given in unsolicited SSO authentication request.\");\n            throw new MessageDecodingException(\"Missing providerId\");\n        }\n        return providerId;\n    }\n}\n","sourceCodeStart":145,"sourceCodeEnd":177,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-saml-idp-web/src/main/java/org/apereo/cas/support/saml/web/idp/profile/SamlIdPInitiatedProfileHandlerController.java#L145-L177","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Include the shire parameter in the unsolicited SSO URL: ?providerId=...&shire=https://sp.example.org/acs.","Set responseLocation (or location) on the SamlRegisteredService so the ACS URL can be derived.","Fix the providerId so the correct registered service with a configured location is matched.","Verify the service is actually found in the service registry (enabled, valid date range)."],"exampleFix":"// before\n<a href=\"/cas/idp/profile/SAML2/Unsolicited/SSO?providerId=sp.example.org\">\n// after\n<a href=\"/cas/idp/profile/SAML2/Unsolicited/SSO?providerId=sp.example.org&shire=https://sp.example.org/Shibboleth.sso/SAML2/POST\">","handlingStrategy":"validation","validationCode":"val shire = request.getParameter(\"shire\");\nval service = servicesManager.findServiceBy(providerId, SamlRegisteredService.class);\nif (StringUtils.isBlank(shire) && (service == null || (StringUtils.isBlank(service.getResponseLocation()) && StringUtils.isBlank(service.getLocation()))))\n    throw new IllegalArgumentException(\"shire parameter or service ACS location required\");","typeGuard":"boolean hasAcsLocation(SamlRegisteredService s) { return s != null && (StringUtils.isNotBlank(s.getLocation()) || StringUtils.isNotBlank(s.getResponseLocation())); }","tryCatchPattern":"try {\n    return controller.handleUnsolicitedSsoRequest(request, response);\n} catch (MessageDecodingException e) {\n    redirect(\"/error?reason=missing-shire&providerId=\" + providerId);\n}","preventionTips":["Always include shire (ACS URL) in IdP-initiated SSO links.","Configure responseLocation/location on every registered SAML service.","Validate the full unsolicited SSO URL template with each SP during onboarding.","Store canonical SSO link templates per SP in documentation."],"tags":["saml","unsolicited-sso","acs-url","configuration"],"backgroundTag":"missing-required-argument","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}