2
0

metadata.adoc 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. [[servlet-saml2login-metadata]]
  2. = Saml 2.0 Metadata
  3. Spring Security can <<parsing-asserting-party-metadata,parse asserting party metadata>> to produce an `AssertingPartyMetadata` instance as well as <<publishing-relying-party-metadata,publish relying party metadata>> from a `RelyingPartyRegistration` instance.
  4. [[parsing-asserting-party-metadata]]
  5. == Parsing `<saml2:IDPSSODescriptor>` metadata
  6. You can parse an asserting party's metadata xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistrationrepository[using `RelyingPartyRegistrations`].
  7. When using the OpenSAML vendor support, the resulting `AssertingPartyMetadata` will be of type `OpenSamlAssertingPartyDetails`.
  8. This means you'll be able to do get the underlying OpenSAML XMLObject by doing the following:
  9. [tabs]
  10. ======
  11. Java::
  12. +
  13. [source,java,role="primary"]
  14. ----
  15. OpenSamlAssertingPartyDetails details = (OpenSamlAssertingPartyDetails)
  16. registration.getAssertingPartyMetadata();
  17. EntityDescriptor openSamlEntityDescriptor = details.getEntityDescriptor();
  18. ----
  19. Kotlin::
  20. +
  21. [source,kotlin,role="secondary"]
  22. ----
  23. val details: OpenSamlAssertingPartyDetails =
  24. registration.getAssertingPartyMetadata() as OpenSamlAssertingPartyDetails
  25. val openSamlEntityDescriptor: EntityDescriptor = details.getEntityDescriptor()
  26. ----
  27. ======
  28. [[using-assertingpartymetadatarepository]]
  29. === Using `AssertingPartyMetadataRepository`
  30. You can also be more targeted than `RelyingPartyRegistrations` by using `AssertingPartyMetadataRepository`, an interface that allows for only retrieving the asserting party metadata.
  31. This allows three valuable features:
  32. * Implementations can refresh asserting party metadata in an expiry-aware fashion
  33. * Implementations of `RelyingPartyRegistrationRepository` can more easily articulate a relationship between a relying party and its one or many corresponding asserting parties
  34. * Implementations can verify metadata signatures
  35. For example, `OpenSaml4AssertingPartyMetadataRepository` uses OpenSAML's `MetadataResolver`, and API whose implementations regularly refresh the underlying metadata in an expiry-aware fashion.
  36. This means that you can now create a refreshable `RelyingPartyRegistrationRepository` in just a few lines of code:
  37. [tabs]
  38. ======
  39. Java::
  40. +
  41. [source,java,role="primary"]
  42. ----
  43. @Component
  44. public class RefreshableRelyingPartyRegistrationRepository
  45. implements IterableRelyingPartyRegistrationRepository {
  46. private final AssertingPartyMetadataRepository metadata =
  47. OpenSaml5AssertingPartyMetadataRepository
  48. .fromTrustedMetadataLocation("https://idp.example.org/metadata").build();
  49. @Override
  50. public RelyingPartyRegistration findByRegistrationId(String registrationId) {
  51. AssertingPartyMetadata metadata = this.metadata.findByEntityId(registrationId);
  52. if (metadata == null) {
  53. return null;
  54. }
  55. return applyRelyingParty(metadata);
  56. }
  57. @Override
  58. public Iterator<RelyingPartyRegistration> iterator() {
  59. return StreamSupport.stream(this.metadata.spliterator(), false)
  60. .map(this::applyRelyingParty).iterator();
  61. }
  62. private RelyingPartyRegistration applyRelyingParty(AssertingPartyMetadata metadata) {
  63. return RelyingPartyRegistration.withAssertingPartyMetadata(metadata)
  64. // apply any relying party configuration
  65. .build();
  66. }
  67. }
  68. ----
  69. Kotlin::
  70. +
  71. [source,kotlin,role="secondary"]
  72. ----
  73. @Component
  74. class RefreshableRelyingPartyRegistrationRepository : IterableRelyingPartyRegistrationRepository {
  75. private val metadata: AssertingPartyMetadataRepository =
  76. OpenSaml5AssertingPartyMetadataRepository.fromTrustedMetadataLocation(
  77. "https://idp.example.org/metadata").build()
  78. fun findByRegistrationId(registrationId:String?): RelyingPartyRegistration {
  79. val metadata = this.metadata.findByEntityId(registrationId)
  80. if (metadata == null) {
  81. return null
  82. }
  83. return applyRelyingParty(metadata)
  84. }
  85. fun iterator(): Iterator<RelyingPartyRegistration> {
  86. return StreamSupport.stream(this.metadata.spliterator(), false)
  87. .map(this::applyRelyingParty).iterator()
  88. }
  89. private fun applyRelyingParty(metadata: AssertingPartyMetadata): RelyingPartyRegistration {
  90. val details: AssertingPartyMetadata = metadata as AssertingPartyMetadata
  91. return RelyingPartyRegistration.withAssertingPartyMetadata(details)
  92. // apply any relying party configuration
  93. .build()
  94. }
  95. }
  96. ----
  97. ======
  98. [TIP]
  99. `OpenSaml4AssertingPartyMetadataRepository` also ships with a constructor so you can provide a custom `MetadataResolver`. Since the underlying `MetadataResolver` is doing the expiring and refreshing, if you use the constructor directly, you will only get these features by providing an implementation that does so.
  100. === Verifying Metadata Signatures
  101. You can also verify metadata signatures using `OpenSaml4AssertingPartyMetadataRepository` by providing the appropriate set of ``Saml2X509Credential``s as follows:
  102. [tabs]
  103. ======
  104. Java::
  105. +
  106. [source,java,role="primary"]
  107. ----
  108. OpenSaml5AssertingPartyMetadataRepository.withMetadataLocation("https://idp.example.org/metadata")
  109. .verificationCredentials((c) -> c.add(myVerificationCredential))
  110. .build();
  111. ----
  112. Kotlin::
  113. +
  114. [source,kotlin,role="secondary"]
  115. ----
  116. OpenSaml5AssertingPartyMetadataRepository.withMetadataLocation("https://idp.example.org/metadata")
  117. .verificationCredentials({ c : Collection<Saml2X509Credential> ->
  118. c.add(myVerificationCredential) })
  119. .build()
  120. ----
  121. ======
  122. [NOTE]
  123. If no credentials are provided, the component will not perform signature validation.
  124. [[publishing-relying-party-metadata]]
  125. == Producing `<saml2:SPSSODescriptor>` Metadata
  126. You can publish a metadata endpoint using the `saml2Metadata` DSL method, as you'll see below:
  127. [tabs]
  128. ======
  129. Java::
  130. +
  131. [source,java,role="primary"]
  132. ----
  133. http
  134. // ...
  135. .saml2Login(withDefaults())
  136. .saml2Metadata(withDefaults());
  137. ----
  138. Kotlin::
  139. +
  140. [source,kotlin,role="secondary"]
  141. ----
  142. http {
  143. //...
  144. saml2Login { }
  145. saml2Metadata { }
  146. }
  147. ----
  148. ======
  149. You can use this metadata endpoint to register your relying party with your asserting party.
  150. This is often as simple as finding the correct form field to supply the metadata endpoint.
  151. By default, the metadata endpoint is `+/saml2/metadata+`, though it also responds to `+/saml2/metadata/{registrationId}+` and `+/saml2/service-provider-metadata/{registrationId}+`.
  152. You can change this by calling the `metadataUrl` method in the DSL:
  153. [tabs]
  154. ======
  155. Java::
  156. +
  157. [source,java,role="primary"]
  158. ----
  159. .saml2Metadata((saml2) -> saml2.metadataUrl("/saml/metadata"))
  160. ----
  161. Kotlin::
  162. +
  163. [source,kotlin,role="secondary"]
  164. ----
  165. saml2Metadata {
  166. metadataUrl = "/saml/metadata"
  167. }
  168. ----
  169. ======
  170. == Changing the Way a `RelyingPartyRegistration` Is Looked Up
  171. If you have a different strategy for identifying which `RelyingPartyRegistration` to use, you can configure your own `Saml2MetadataResponseResolver` like the one below:
  172. [tabs]
  173. ======
  174. Java::
  175. +
  176. [source,java,role="primary"]
  177. ----
  178. @Bean
  179. Saml2MetadataResponseResolver metadataResponseResolver(RelyingPartyRegistrationRepository registrations) {
  180. RequestMatcherMetadataResponseResolver metadata = new RequestMatcherMetadataResponseResolver(
  181. (id) -> registrations.findByRegistrationId("relying-party"));
  182. metadata.setMetadataFilename("metadata.xml");
  183. return metadata;
  184. }
  185. ----
  186. Kotlin::
  187. +
  188. [source,kotlin,role="secondary"]
  189. ----
  190. @Bean
  191. fun metadataResponseResolver(val registrations: RelyingPartyRegistrationRepository): Saml2MetadataResponseResolver {
  192. val metadata = new RequestMatcherMetadataResponseResolver(
  193. id: String -> registrations.findByRegistrationId("relying-party"))
  194. metadata.setMetadataFilename("metadata.xml")
  195. return metadata
  196. }
  197. ----
  198. ======