{"record":{"id":"cd4f955437919537","repo":"spring-projects/spring-security","slug":"metadata-response-is-missing-a-singlesignonservice","errorCode":null,"errorMessage":"Metadata response is missing a SingleSignOnService, necessary for sending AuthnRequests","messagePattern":"Metadata response is missing a SingleSignOnService, necessary for sending AuthnRequests","errorType":"exception","errorClass":"Saml2Exception","httpStatus":null,"severity":"error","filePath":"saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlAssertingPartyDetails.java","lineNumber":130,"sourceCode":"\t\tif (verification.isEmpty()) {\n\t\t\tthrow new Saml2Exception(\n\t\t\t\t\t\"Metadata response is missing verification certificates, necessary for verifying SAML assertions\");\n\t\t}\n\t\tString entityId = entity.getEntityID();\n\t\tAssert.notNull(entityId, \"EntityDescriptor#EntityID cannot be null\");\n\t\tOpenSamlAssertingPartyDetails.Builder builder = new OpenSamlAssertingPartyDetails.Builder(entity)\n\t\t\t.entityId(entityId)\n\t\t\t.wantAuthnRequestsSigned(Boolean.TRUE.equals(idpssoDescriptor.getWantAuthnRequestsSigned()))\n\t\t\t.verificationX509Credentials((c) -> c.addAll(verification))\n\t\t\t.encryptionX509Credentials((c) -> c.addAll(encryption));\n\n\t\tList<SigningMethod> signingMethods = signingMethods(idpssoDescriptor);\n\t\tfor (SigningMethod method : signingMethods) {\n\t\t\tAssert.notNull(method.getAlgorithm(), \"EntityDescriptor declares a SigningMethod with no value\");\n\t\t\tbuilder.signingAlgorithms((algorithms) -> algorithms.add(method.getAlgorithm()));\n\t\t}\n\t\tif (idpssoDescriptor.getSingleSignOnServices().isEmpty()) {\n\t\t\tthrow new Saml2Exception(\n\t\t\t\t\t\"Metadata response is missing a SingleSignOnService, necessary for sending AuthnRequests\");\n\t\t}\n\t\tfor (SingleSignOnService singleSignOnService : idpssoDescriptor.getSingleSignOnServices()) {\n\t\t\tSaml2MessageBinding binding;\n\t\t\tif (Saml2MessageBinding.POST.getUrn().equals(singleSignOnService.getBinding())) {\n\t\t\t\tbinding = Saml2MessageBinding.POST;\n\t\t\t}\n\t\t\telse if (Saml2MessageBinding.REDIRECT.getUrn().equals(singleSignOnService.getBinding())) {\n\t\t\t\tbinding = Saml2MessageBinding.REDIRECT;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString location = singleSignOnService.getLocation();\n\t\t\tAssert.notNull(location, \"EntityDescriptor has a SingleSignOnService declaration, but no Location\");\n\t\t\tbuilder.singleSignOnServiceLocation(location).singleSignOnServiceBinding(binding);\n\t\t\tbreak;\n\t\t}","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlAssertingPartyDetails.java#L112-L148","documentation":"Spring Security SAML2 throws this Saml2Exception when the parsed IdP metadata has an IDPSSODescriptor but no SingleSignOnService entries. The SingleSignOnService location/binding is what the application uses to send AuthnRequest messages; without it the RelyingPartyRegistration cannot be constructed.","triggerScenarios":"RelyingPartyRegistrations.fromMetadata/fromMetadataLocation or OpenSamlAssertingPartyDetails.withEntityDescriptor on metadata where idpssoDescriptor.getSingleSignOnServices() is empty.","commonSituations":"Hand-crafted metadata containing only certificates and entityId; IdP published metadata limited to SLO/logout services; truncated or partially downloaded metadata; wrong entity (e.g. SP metadata fed as IdP metadata).","solutions":["Use the IdP's official metadata URL/file that includes <md:SingleSignOnService Binding=\"...urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"...\"/> and re-import","Manually set it via assertingParty(details -> details.singleSignOnServiceLocation(\"https://idp/sso\").singleSignOnServiceBinding(Saml2MessageBinding.POST))","Confirm the metadata document is IdP metadata (IDPSSODescriptor), not SP metadata","If metadata is generated, fix the generator to emit at least one SingleSignOnService"],"exampleFix":"// before (metadata lacks SSO service -> error)\nRelyingPartyRegistration r = RelyingPartyRegistrations.fromMetadataLocation(\"https://idp/meta\").build();\n// after: declare SSO service manually\nRelyingPartyRegistration r = RelyingPartyRegistration.withAssertingPartyMetadata(party -> party\n    .entityId(\"https://idp.example.com/sso\")\n    .singleSignOnServiceLocation(\"https://idp.example.com/sso/SSO\")\n    .singleSignOnServiceBinding(Saml2MessageBinding.POST))\n    .registrationId(\"idp\")\n    .build();","handlingStrategy":"validation","validationCode":"Document doc = parseXml(metadataBytes);\nNodeList sso = doc.getElementsByTagNameNS(\"urn:oasis:names:tc:SAML:2.0:metadata\", \"SingleSignOnService\");\nif (sso.getLength() == 0) throw new IllegalArgumentException(\"Metadata has no SingleSignOnService\");","typeGuard":null,"tryCatchPattern":"try {\n    return RelyingPartyRegistrations.fromMetadataLocation(location).build();\n} catch (Saml2Exception ex) {\n    // supply SSO location manually from IdP admin console values\n    return builderWithManualSsoEndpoint(ex);\n}","preventionTips":["Never hand-write metadata without a SingleSignOnService element; prefer the IdP's published metadata","Check that the URL you import is IdP metadata, not SP metadata","Assert at startup (fail fast) that every registration resolves an SSO location"],"tags":["saml2","metadata","sso-service","spring-security"],"backgroundTag":"missing-required-config-field","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}