authentication-requests.adoc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  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. okta:
  86. identityprovider:
  87. entity-id: ...
  88. singlesignon.sign-request: false
  89. ----
  90. Java::
  91. +
  92. [source,java,role="secondary"]
  93. ----
  94. RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta")
  95. // ...
  96. .assertingPartyMetadata(party -> party
  97. // ...
  98. .wantAuthnRequestsSigned(false)
  99. )
  100. .build();
  101. ----
  102. Kotlin::
  103. +
  104. [source,kotlin,role="secondary"]
  105. ----
  106. var relyingPartyRegistration: RelyingPartyRegistration =
  107. RelyingPartyRegistration.withRegistrationId("okta")
  108. // ...
  109. .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party
  110. // ...
  111. .wantAuthnRequestsSigned(false)
  112. }
  113. .build()
  114. ----
  115. ======
  116. Otherwise, you will need to specify a private key to `RelyingPartyRegistration#signingX509Credentials` so that Spring Security can sign the `<saml2:AuthnRequest>` before sending.
  117. [[servlet-saml2login-sp-initiated-factory-algorithm]]
  118. 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.
  119. You can configure the algorithm based on the asserting party's xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistrationrepository[metadata using `RelyingPartyRegistrations`].
  120. Or, you can provide it manually:
  121. [tabs]
  122. ======
  123. Java::
  124. +
  125. [source,java,role="primary"]
  126. ----
  127. String metadataLocation = "classpath:asserting-party-metadata.xml";
  128. RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)
  129. // ...
  130. .assertingPartyMetadata((party) -> party
  131. // ...
  132. .signingAlgorithms((sign) -> sign.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512))
  133. )
  134. .build();
  135. ----
  136. Kotlin::
  137. +
  138. [source,kotlin,role="secondary"]
  139. ----
  140. var metadataLocation = "classpath:asserting-party-metadata.xml"
  141. var relyingPartyRegistration: RelyingPartyRegistration =
  142. RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)
  143. // ...
  144. .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party
  145. // ...
  146. .signingAlgorithms { sign: MutableList<String?> ->
  147. sign.add(
  148. SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512
  149. )
  150. }
  151. }
  152. .build()
  153. ----
  154. ======
  155. NOTE: The snippet above uses the OpenSAML `SignatureConstants` class to supply the algorithm name.
  156. But, that's just for convenience.
  157. Since the datatype is `String`, you can supply the name of the algorithm directly.
  158. [[servlet-saml2login-sp-initiated-factory-binding]]
  159. Some asserting parties require that the `<saml2:AuthnRequest>` be POSTed.
  160. This can be configured automatically via `RelyingPartyRegistrations`, or you can supply it manually, like so:
  161. [tabs]
  162. ======
  163. Java::
  164. +
  165. [source,java,role="primary"]
  166. ----
  167. RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta")
  168. // ...
  169. .assertingPartyMetadata(party -> party
  170. // ...
  171. .singleSignOnServiceBinding(Saml2MessageBinding.POST)
  172. )
  173. .build();
  174. ----
  175. Kotlin::
  176. +
  177. [source,kotlin,role="secondary"]
  178. ----
  179. var relyingPartyRegistration: RelyingPartyRegistration? =
  180. RelyingPartyRegistration.withRegistrationId("okta")
  181. // ...
  182. .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party
  183. // ...
  184. .singleSignOnServiceBinding(Saml2MessageBinding.POST)
  185. }
  186. .build()
  187. ----
  188. ======
  189. [[servlet-saml2login-sp-initiated-factory-custom-authnrequest]]
  190. == Customizing OpenSAML's `AuthnRequest` Instance
  191. There are a number of reasons that you may want to adjust an `AuthnRequest`.
  192. For example, you may want `ForceAuthN` to be set to `true`, which Spring Security sets to `false` by default.
  193. You can customize elements of OpenSAML's `AuthnRequest` by publishing an `OpenSaml4AuthenticationRequestResolver` as a `@Bean`, like so:
  194. [tabs]
  195. ======
  196. Java::
  197. +
  198. [source,java,role="primary"]
  199. ----
  200. @Bean
  201. Saml2AuthenticationRequestResolver authenticationRequestResolver(RelyingPartyRegistrationRepository registrations) {
  202. RelyingPartyRegistrationResolver registrationResolver =
  203. new DefaultRelyingPartyRegistrationResolver(registrations);
  204. OpenSaml4AuthenticationRequestResolver authenticationRequestResolver =
  205. new OpenSaml4AuthenticationRequestResolver(registrationResolver);
  206. authenticationRequestResolver.setAuthnRequestCustomizer((context) -> context
  207. .getAuthnRequest().setForceAuthn(true));
  208. return authenticationRequestResolver;
  209. }
  210. ----
  211. Kotlin::
  212. +
  213. [source,kotlin,role="secondary"]
  214. ----
  215. @Bean
  216. fun authenticationRequestResolver(registrations : RelyingPartyRegistrationRepository) : Saml2AuthenticationRequestResolver {
  217. val registrationResolver : RelyingPartyRegistrationResolver =
  218. new DefaultRelyingPartyRegistrationResolver(registrations)
  219. val authenticationRequestResolver : OpenSaml4AuthenticationRequestResolver =
  220. new OpenSaml4AuthenticationRequestResolver(registrationResolver)
  221. authenticationRequestResolver.setAuthnRequestCustomizer((context) -> context
  222. .getAuthnRequest().setForceAuthn(true))
  223. return authenticationRequestResolver
  224. }
  225. ----
  226. ======