authentication-requests.adoc 8.9 KB

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