As stated earlier, Spring Security’s SAML 2.0 support produces a <saml2:AuthnRequest>
to commence authentication with the asserting party.
Spring Security achieves this in part by registering the Saml2WebSsoAuthenticationRequestFilter
in the filter chain.
This filter by default responds to endpoint /saml2/authenticate/{registrationId}
.
For example, if you were deployed to https://rp.example.com
and you gave your registration an ID of okta
, you could navigate to:
and the result would be a redirect that included a SAMLRequest
parameter containing the signed, deflated, and encoded <saml2:AuthnRequest>
.
Saml2WebSsoAuthenticationRequestFilter
uses an Saml2AuthenticationRequestRepository
to persist an AbstractSaml2AuthenticationRequest
instance before sending the <saml2:AuthnRequest>
to the asserting party.
Additionally, Saml2WebSsoAuthenticationFilter
and Saml2AuthenticationTokenConverter
use an Saml2AuthenticationRequestRepository
to load any AbstractSaml2AuthenticationRequest
as part of authenticating the <saml2:Response>
.
By default, Spring Security uses an HttpSessionSaml2AuthenticationRequestRepository
, which stores the AbstractSaml2AuthenticationRequest
in the HttpSession
.
If you have a custom implementation of Saml2AuthenticationRequestRepository
, you may configure it by exposing it as a @Bean
as shown in the following example:
- Java
-
@Bean Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository() { return new CustomSaml2AuthenticationRequestRepository(); }
- Kotlin
-
@Bean open fun authenticationRequestRepository(): Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> { return CustomSaml2AuthenticationRequestRepository() }
By default, Spring Security signs each <saml2:AuthnRequest>
and send it as a GET to the asserting party.
Many asserting parties don’t require a signed <saml2:AuthnRequest>
.
This can be configured automatically via RelyingPartyRegistrations
, or you can supply it manually, like so:
- Boot
-
spring: security: saml2: relyingparty: okta: identityprovider: entity-id: ... singlesignon.sign-request: false
- Java
-
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta") // ... .assertingPartyDetails(party -> party // ... .wantAuthnRequestsSigned(false) ) .build();
- Kotlin
-
var relyingPartyRegistration: RelyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta") // ... .assertingPartyDetails { party: AssertingPartyDetails.Builder -> party // ... .wantAuthnRequestsSigned(false) } .build();
Otherwise, you will need to specify a private key to RelyingPartyRegistration#signingX509Credentials
so that Spring Security can sign the <saml2:AuthnRequest>
before sending.
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.
You can configure the algorithm based on the asserting party’s metadata using RelyingPartyRegistrations
.
Or, you can provide it manually:
- Java
-
String metadataLocation = "classpath:asserting-party-metadata.xml"; RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations.fromMetadataLocation(metadataLocation) // ... .assertingPartyDetails((party) -> party // ... .signingAlgorithms((sign) -> sign.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512)) ) .build();
- Kotlin
-
var metadataLocation = "classpath:asserting-party-metadata.xml" var relyingPartyRegistration: RelyingPartyRegistration = RelyingPartyRegistrations.fromMetadataLocation(metadataLocation) // ... .assertingPartyDetails { party: AssertingPartyDetails.Builder -> party // ... .signingAlgorithms { sign: MutableList<String?> -> sign.add( SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512 ) } } .build();
Note
|
The snippet above uses the OpenSAML SignatureConstants class to supply the algorithm name.
But, that’s just for convenience.
Since the datatype is String , you can supply the name of the algorithm directly.
|
Some asserting parties require that the <saml2:AuthnRequest>
be POSTed.
This can be configured automatically via RelyingPartyRegistrations
, or you can supply it manually, like so:
- Java
-
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta") // ... .assertingPartyDetails(party -> party // ... .singleSignOnServiceBinding(Saml2MessageBinding.POST) ) .build();
- Kotlin
-
var relyingPartyRegistration: RelyingPartyRegistration? = RelyingPartyRegistration.withRegistrationId("okta") // ... .assertingPartyDetails { party: AssertingPartyDetails.Builder -> party // ... .singleSignOnServiceBinding(Saml2MessageBinding.POST) } .build()
There are a number of reasons that you may want to adjust an AuthnRequest
.
For example, you may want ForceAuthN
to be set to true
, which Spring Security sets to false
by default.
You can customize elements of OpenSAML’s AuthnRequest
by publishing an OpenSaml4AuthenticationRequestResolver
as a @Bean
, like so:
- Java
-
@Bean Saml2AuthenticationRequestResolver authenticationRequestResolver(RelyingPartyRegistrationRepository registrations) { RelyingPartyRegistrationResolver registrationResolver = new DefaultRelyingPartyRegistrationResolver(registrations); OpenSaml4AuthenticationRequestResolver authenticationRequestResolver = new OpenSaml4AuthenticationRequestResolver(registrationResolver); authenticationRequestResolver.setAuthnRequestCustomizer((context) -> context .getAuthnRequest().setForceAuthn(true)); return authenticationRequestResolver; }
- Kotlin
-
@Bean fun authenticationRequestResolver(registrations : RelyingPartyRegistrationRepository) : Saml2AuthenticationRequestResolver { val registrationResolver : RelyingPartyRegistrationResolver = new DefaultRelyingPartyRegistrationResolver(registrations) val authenticationRequestResolver : OpenSaml4AuthenticationRequestResolver = new OpenSaml4AuthenticationRequestResolver(registrationResolver) authenticationRequestResolver.setAuthnRequestCustomizer((context) -> context .getAuthnRequest().setForceAuthn(true)) return authenticationRequestResolver }