authentication-requests.adoc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. [[servlet-saml2login-sp-initiated-factory]]
  2. = Producing ``<saml2:AuthnRequest>``s
  3. As stated earlier, Spring Security's SAML 2.0 support produces a `<saml2:AuthnRequest>` to commence authentication with the asserting party.
  4. Spring Security achieves this in part by registering the `Saml2WebSsoAuthenticationRequestFilter` in the filter chain.
  5. This filter by default responds to the endpoints `+/saml2/authenticate/{registrationId}+` and `+/saml2/authenticate?registrationId={registrationId}+`.
  6. For example, if you were deployed to `https://rp.example.com` and you gave your registration an ID of `okta`, you could navigate to:
  7. `https://rp.example.org/saml2/authenticate/okta`
  8. and the result would be a redirect that included a `SAMLRequest` parameter containing the signed, deflated, and encoded `<saml2:AuthnRequest>`.
  9. [[configuring-authentication-request-uri]]
  10. == Configuring the `<saml2:AuthnRequest>` Endpoint
  11. To configure the endpoint differently from the default, you can set the value in `saml2Login`:
  12. [tabs]
  13. ======
  14. Java::
  15. +
  16. [source,java,role="primary"]
  17. ----
  18. @Bean
  19. SecurityFilterChain filterChain(HttpSecurity http) {
  20. http
  21. .saml2Login((saml2) -> saml2
  22. .authenticationRequestUriQuery("/custom/auth/sso?peerEntityID={registrationId}")
  23. );
  24. return new CustomSaml2AuthenticationRequestRepository();
  25. }
  26. ----
  27. Kotlin::
  28. +
  29. [source,kotlin,role="secondary"]
  30. ----
  31. @Bean
  32. fun filterChain(http: HttpSecurity): SecurityFilterChain {
  33. http {
  34. saml2Login {
  35. authenticationRequestUriQuery = "/custom/auth/sso?peerEntityID={registrationId}"
  36. }
  37. }
  38. return CustomSaml2AuthenticationRequestRepository()
  39. }
  40. ----
  41. ======
  42. [[servlet-saml2login-store-authn-request]]
  43. == Changing How the `<saml2:AuthnRequest>` Gets Stored
  44. `Saml2WebSsoAuthenticationRequestFilter` uses an `Saml2AuthenticationRequestRepository` to persist an `AbstractSaml2AuthenticationRequest` instance before xref:servlet/saml2/login/authentication-requests.adoc#servlet-saml2login-sp-initiated-factory[sending the `<saml2:AuthnRequest>`] to the asserting party.
  45. Additionally, `Saml2WebSsoAuthenticationFilter` and `Saml2AuthenticationTokenConverter` use an `Saml2AuthenticationRequestRepository` to load any `AbstractSaml2AuthenticationRequest` as part of xref:servlet/saml2/login/authentication.adoc#servlet-saml2login-authenticate-responses[authenticating the `<saml2:Response>`].
  46. By default, Spring Security uses an `HttpSessionSaml2AuthenticationRequestRepository`, which stores the `AbstractSaml2AuthenticationRequest` in the `HttpSession`.
  47. If you have a custom implementation of `Saml2AuthenticationRequestRepository`, you may configure it by exposing it as a `@Bean` as shown in the following example:
  48. [tabs]
  49. ======
  50. Java::
  51. +
  52. [source,java,role="primary"]
  53. ----
  54. @Bean
  55. Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository() {
  56. return new CustomSaml2AuthenticationRequestRepository();
  57. }
  58. ----
  59. Kotlin::
  60. +
  61. [source,kotlin,role="secondary"]
  62. ----
  63. @Bean
  64. open fun authenticationRequestRepository(): Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> {
  65. return CustomSaml2AuthenticationRequestRepository()
  66. }
  67. ----
  68. ======
  69. [[servlet-saml2login-sp-initiated-factory-signing]]
  70. == Changing How the `<saml2:AuthnRequest>` Gets Sent
  71. By default, Spring Security signs each `<saml2:AuthnRequest>` and send it as a GET to the asserting party.
  72. Many asserting parties don't require a signed `<saml2:AuthnRequest>`.
  73. This can be configured automatically via `RelyingPartyRegistrations`, or you can supply it manually, like so:
  74. .Not Requiring Signed AuthnRequests
  75. [tabs]
  76. ======
  77. Boot::
  78. +
  79. [source,yaml,role="primary"]
  80. ----
  81. spring:
  82. security:
  83. saml2:
  84. relyingparty:
  85. registration:
  86. okta:
  87. assertingparty:
  88. entity-id: ...
  89. singlesignon.sign-request: false
  90. ----
  91. Java::
  92. +
  93. [source,java,role="secondary"]
  94. ----
  95. RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta")
  96. // ...
  97. .assertingPartyMetadata(party -> party
  98. // ...
  99. .wantAuthnRequestsSigned(false)
  100. )
  101. .build();
  102. ----
  103. Kotlin::
  104. +
  105. [source,kotlin,role="secondary"]
  106. ----
  107. var relyingPartyRegistration: RelyingPartyRegistration =
  108. RelyingPartyRegistration.withRegistrationId("okta")
  109. // ...
  110. .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party
  111. // ...
  112. .wantAuthnRequestsSigned(false)
  113. }
  114. .build()
  115. ----
  116. ======
  117. Otherwise, you will need to specify a private key to `RelyingPartyRegistration#signingX509Credentials` so that Spring Security can sign the `<saml2:AuthnRequest>` before sending.
  118. [[servlet-saml2login-sp-initiated-factory-algorithm]]
  119. By default, Spring Security will sign the `<saml2:AuthnRequest>` using `rsa-sha256`, though some asserting parties will require a different algorithm, as indicated in their metadata.
  120. You can configure the algorithm based on the asserting party's xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistrationrepository[metadata using `RelyingPartyRegistrations`].
  121. Or, you can provide it manually:
  122. [tabs]
  123. ======
  124. Java::
  125. +
  126. [source,java,role="primary"]
  127. ----
  128. String metadataLocation = "classpath:asserting-party-metadata.xml";
  129. RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)
  130. // ...
  131. .assertingPartyMetadata((party) -> party
  132. // ...
  133. .signingAlgorithms((sign) -> sign.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512))
  134. )
  135. .build();
  136. ----
  137. Kotlin::
  138. +
  139. [source,kotlin,role="secondary"]
  140. ----
  141. var metadataLocation = "classpath:asserting-party-metadata.xml"
  142. var relyingPartyRegistration: RelyingPartyRegistration =
  143. RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)
  144. // ...
  145. .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party
  146. // ...
  147. .signingAlgorithms { sign: MutableList<String?> ->
  148. sign.add(
  149. SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512
  150. )
  151. }
  152. }
  153. .build()
  154. ----
  155. ======
  156. NOTE: The snippet above uses the OpenSAML `SignatureConstants` class to supply the algorithm name.
  157. But, that's just for convenience.
  158. Since the datatype is `String`, you can supply the name of the algorithm directly.
  159. [[servlet-saml2login-sp-initiated-factory-binding]]
  160. Some asserting parties require that the `<saml2:AuthnRequest>` be POSTed.
  161. This can be configured automatically via `RelyingPartyRegistrations`, or you can supply it manually, like so:
  162. [tabs]
  163. ======
  164. Java::
  165. +
  166. [source,java,role="primary"]
  167. ----
  168. RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta")
  169. // ...
  170. .assertingPartyMetadata(party -> party
  171. // ...
  172. .singleSignOnServiceBinding(Saml2MessageBinding.POST)
  173. )
  174. .build();
  175. ----
  176. Kotlin::
  177. +
  178. [source,kotlin,role="secondary"]
  179. ----
  180. var relyingPartyRegistration: RelyingPartyRegistration? =
  181. RelyingPartyRegistration.withRegistrationId("okta")
  182. // ...
  183. .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party
  184. // ...
  185. .singleSignOnServiceBinding(Saml2MessageBinding.POST)
  186. }
  187. .build()
  188. ----
  189. ======
  190. [[servlet-saml2login-sp-initiated-factory-custom-authnrequest]]
  191. == Customizing OpenSAML's `AuthnRequest` Instance
  192. There are a number of reasons that you may want to adjust an `AuthnRequest`.
  193. For example, you may want `ForceAuthN` to be set to `true`, which Spring Security sets to `false` by default.
  194. You can customize elements of OpenSAML's `AuthnRequest` by publishing an `OpenSaml4AuthenticationRequestResolver` as a `@Bean`, like so:
  195. [tabs]
  196. ======
  197. Java::
  198. +
  199. [source,java,role="primary"]
  200. ----
  201. @Bean
  202. Saml2AuthenticationRequestResolver authenticationRequestResolver(RelyingPartyRegistrationRepository registrations) {
  203. RelyingPartyRegistrationResolver registrationResolver =
  204. new DefaultRelyingPartyRegistrationResolver(registrations);
  205. OpenSaml4AuthenticationRequestResolver authenticationRequestResolver =
  206. new OpenSaml4AuthenticationRequestResolver(registrationResolver);
  207. authenticationRequestResolver.setAuthnRequestCustomizer((context) -> context
  208. .getAuthnRequest().setForceAuthn(true));
  209. return authenticationRequestResolver;
  210. }
  211. ----
  212. Kotlin::
  213. +
  214. [source,kotlin,role="secondary"]
  215. ----
  216. @Bean
  217. fun authenticationRequestResolver(registrations : RelyingPartyRegistrationRepository) : Saml2AuthenticationRequestResolver {
  218. val registrationResolver : RelyingPartyRegistrationResolver =
  219. new DefaultRelyingPartyRegistrationResolver(registrations)
  220. val authenticationRequestResolver : OpenSaml4AuthenticationRequestResolver =
  221. new OpenSaml4AuthenticationRequestResolver(registrationResolver)
  222. authenticationRequestResolver.setAuthnRequestCustomizer((context) -> context
  223. .getAuthnRequest().setForceAuthn(true))
  224. return authenticationRequestResolver
  225. }
  226. ----
  227. ======