Update to use OpenSaml4AuthenticationProvider

Closes gh-10013
This commit is contained in:
Josh Cummings 2021-06-28 12:51:55 -06:00
parent 8d3e58f074
commit 5940b8dee7
No known key found for this signature in database
GPG Key ID: 49EF60DD7FF83443
1 changed files with 31 additions and 31 deletions

View File

@ -55,7 +55,7 @@ This filter calls its configured `AuthenticationConverter` to create a `Saml2Aut
This converter additionally resolves the <<servlet-saml2login-relyingpartyregistration, `RelyingPartyRegistration`>> and supplies it to `Saml2AuthenticationToken`. This converter additionally resolves the <<servlet-saml2login-relyingpartyregistration, `RelyingPartyRegistration`>> and supplies it to `Saml2AuthenticationToken`.
image:{icondir}/number_2.png[] Next, the filter passes the token to its configured <<servlet-authentication-providermanager,`AuthenticationManager`>>. image:{icondir}/number_2.png[] Next, the filter passes the token to its configured <<servlet-authentication-providermanager,`AuthenticationManager`>>.
By default, it will use the <<servlet-saml2login-architecture,`OpenSamlAuthenticationProvider`>>. By default, it will use the <<servlet-saml2login-architecture,`OpenSAML authentication provider`>>.
image:{icondir}/number_3.png[] If authentication fails, then __Failure__ image:{icondir}/number_3.png[] If authentication fails, then __Failure__
@ -150,9 +150,9 @@ To achieve this, any interfaces or classes where Spring Security uses OpenSAML i
This makes it possible for you to switch out OpenSAML for some other library or even an unsupported version of OpenSAML. This makes it possible for you to switch out OpenSAML for some other library or even an unsupported version of OpenSAML.
As a natural outcome of the above two goals, Spring Security's SAML API is quite small relative to other modules. As a natural outcome of the above two goals, Spring Security's SAML API is quite small relative to other modules.
Instead, classes like `OpenSamlAuthenticationRequestFactory` and `OpenSamlAuthenticationProvider` expose `Converter` s that customize various steps in the authentication process. Instead, classes like `OpenSaml4AuthenticationRequestFactory` and `OpenSaml4AuthenticationProvider` expose `Converter` s that customize various steps in the authentication process.
For example, once your application receives a `SAMLResponse` and delegates to `Saml2WebSsoAuthenticationFilter`, the filter will delegate to `OpenSamlAuthenticationProvider`. For example, once your application receives a `SAMLResponse` and delegates to `Saml2WebSsoAuthenticationFilter`, the filter will delegate to `OpenSaml4AuthenticationProvider`.
.Authenticating an OpenSAML `Response` .Authenticating an OpenSAML `Response`
image:{figures}/opensamlauthenticationprovider.png[] image:{figures}/opensamlauthenticationprovider.png[]
@ -161,7 +161,7 @@ This figure builds off of the <<servlet-saml2login-authentication-saml2webssoaut
image:{icondir}/number_1.png[] The `Saml2WebSsoAuthenticationFilter` formulates the `Saml2AuthenticationToken` and invokes the <<servlet-authentication-providermanager,`AuthenticationManager`>>. image:{icondir}/number_1.png[] The `Saml2WebSsoAuthenticationFilter` formulates the `Saml2AuthenticationToken` and invokes the <<servlet-authentication-providermanager,`AuthenticationManager`>>.
image:{icondir}/number_2.png[] The <<servlet-authentication-providermanager,`AuthenticationManager`>> invokes the `OpenSamlAuthenticationProvider`. image:{icondir}/number_2.png[] The <<servlet-authentication-providermanager,`AuthenticationManager`>> invokes the OpenSAML authentication provider.
image:{icondir}/number_3.png[] The authentication provider deserializes the response into an OpenSAML `Response` and checks its signature. image:{icondir}/number_3.png[] The authentication provider deserializes the response into an OpenSAML `Response` and checks its signature.
If the signature is invalid, authentication fails. If the signature is invalid, authentication fails.
@ -1061,8 +1061,8 @@ Saml2AuthenticationRequestContextResolver authenticationRequestContextResolver()
Saml2AuthenticationRequestFactory authenticationRequestFactory( Saml2AuthenticationRequestFactory authenticationRequestFactory(
AuthnRequestConverter authnRequestConverter) { AuthnRequestConverter authnRequestConverter) {
OpenSamlAuthenticationRequestFactory authenticationRequestFactory = OpenSaml4AuthenticationRequestFactory authenticationRequestFactory =
new OpenSamlAuthenticationRequestFactory(); new OpenSaml4AuthenticationRequestFactory();
authenticationRequestFactory.setAuthenticationRequestContextConverter(authnRequestConverter); authenticationRequestFactory.setAuthenticationRequestContextConverter(authnRequestConverter);
return authenticationRequestFactory; return authenticationRequestFactory;
} }
@ -1087,7 +1087,7 @@ open fun authenticationRequestContextResolver(): Saml2AuthenticationRequestConte
open fun authenticationRequestFactory( open fun authenticationRequestFactory(
authnRequestConverter: AuthnRequestConverter? authnRequestConverter: AuthnRequestConverter?
): Saml2AuthenticationRequestFactory? { ): Saml2AuthenticationRequestFactory? {
val authenticationRequestFactory = OpenSamlAuthenticationRequestFactory() val authenticationRequestFactory = OpenSaml4AuthenticationRequestFactory()
authenticationRequestFactory.setAuthenticationRequestContextConverter(authnRequestConverter) authenticationRequestFactory.setAuthenticationRequestContextConverter(authnRequestConverter)
return authenticationRequestFactory return authenticationRequestFactory
} }
@ -1097,7 +1097,7 @@ open fun authenticationRequestFactory(
[[servlet-saml2login-authenticate-responses]] [[servlet-saml2login-authenticate-responses]]
=== Authenticating `<saml2:Response>` s === Authenticating `<saml2:Response>` s
To verify SAML 2.0 Responses, Spring Security uses <<servlet-saml2login-architecture,`OpenSamlAuthenticationProvider`>> by default. To verify SAML 2.0 Responses, Spring Security uses <<servlet-saml2login-architecture,`OpenSaml4AuthenticationProvider`>> by default.
You can configure this in a number of ways including: You can configure this in a number of ways including:
@ -1112,7 +1112,7 @@ To configure these, you'll use the `saml2Login#authenticationManager` method in
==== Setting a Clock Skew ==== Setting a Clock Skew
It's not uncommon for the asserting and relying parties to have system clocks that aren't perfectly synchronized. It's not uncommon for the asserting and relying parties to have system clocks that aren't perfectly synchronized.
For that reason, you can configure `OpenSamlAuthenticationProvider` 's default assertion validator with some tolerance: For that reason, you can configure `OpenSaml4AuthenticationProvider` 's default assertion validator with some tolerance:
==== ====
.Java .Java
@ -1123,8 +1123,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
OpenSamlAuthenticationProvider authenticationProvider = new OpenSamlAuthenticationProvider(); OpenSaml4AuthenticationProvider authenticationProvider = new OpenSaml4AuthenticationProvider();
authenticationProvider.setAssertionValidator(OpenSamlAuthenticationProvider authenticationProvider.setAssertionValidator(OpenSaml4AuthenticationProvider
.createDefaultAssertionValidator(assertionToken -> { .createDefaultAssertionValidator(assertionToken -> {
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.put(CLOCK_SKEW, Duration.ofMinutes(10).toMillis()); params.put(CLOCK_SKEW, Duration.ofMinutes(10).toMillis());
@ -1150,10 +1150,10 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@EnableWebSecurity @EnableWebSecurity
open class SecurityConfig : WebSecurityConfigurerAdapter() { open class SecurityConfig : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) { override fun configure(http: HttpSecurity) {
val authenticationProvider = OpenSamlAuthenticationProvider() val authenticationProvider = OpenSaml4AuthenticationProvider()
authenticationProvider.setAssertionValidator( authenticationProvider.setAssertionValidator(
OpenSamlAuthenticationProvider OpenSaml4AuthenticationProvider
.createDefaultAssertionValidator(Converter<OpenSamlAuthenticationProvider.AssertionToken, ValidationContext> { .createDefaultAssertionValidator(Converter<OpenSaml4AuthenticationProvider.AssertionToken, ValidationContext> {
val params: MutableMap<String, Any> = HashMap() val params: MutableMap<String, Any> = HashMap()
params[CLOCK_SKEW] = params[CLOCK_SKEW] =
Duration.ofMinutes(10).toMillis() Duration.ofMinutes(10).toMillis()
@ -1190,9 +1190,9 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
OpenSamlAuthenticationProvider authenticationProvider = new OpenSamlAuthenticationProvider(); OpenSaml4AuthenticationProvider authenticationProvider = new OpenSaml4AuthenticationProvider();
authenticationProvider.setResponseAuthenticationConverter(responseToken -> { authenticationProvider.setResponseAuthenticationConverter(responseToken -> {
Saml2Authentication authentication = OpenSamlAuthenticationProvider Saml2Authentication authentication = OpenSaml4AuthenticationProvider
.createDefaultResponseAuthenticationConverter() <1> .createDefaultResponseAuthenticationConverter() <1>
.convert(responseToken); .convert(responseToken);
Assertion assertion = responseToken.getResponse().getAssertions().get(0); Assertion assertion = responseToken.getResponse().getAssertions().get(0);
@ -1221,9 +1221,9 @@ open class SecurityConfig : WebSecurityConfigurerAdapter() {
var userDetailsService: UserDetailsService? = null var userDetailsService: UserDetailsService? = null
override fun configure(http: HttpSecurity) { override fun configure(http: HttpSecurity) {
val authenticationProvider = OpenSamlAuthenticationProvider() val authenticationProvider = OpenSaml4AuthenticationProvider()
authenticationProvider.setResponseAuthenticationConverter { responseToken: OpenSamlAuthenticationProvider.ResponseToken -> authenticationProvider.setResponseAuthenticationConverter { responseToken: OpenSaml4AuthenticationProvider.ResponseToken ->
val authentication = OpenSamlAuthenticationProvider val authentication = OpenSaml4AuthenticationProvider
.createDefaultResponseAuthenticationConverter() <1> .createDefaultResponseAuthenticationConverter() <1>
.convert(responseToken) .convert(responseToken)
val assertion: Assertion = responseToken.response.assertions[0] val assertion: Assertion = responseToken.response.assertions[0]
@ -1248,19 +1248,19 @@ open class SecurityConfig : WebSecurityConfigurerAdapter() {
<3> Third, return a custom authentication that includes the user details <3> Third, return a custom authentication that includes the user details
[NOTE] [NOTE]
It's not required to call `OpenSamlAuthenticationProvider` 's default authentication converter. It's not required to call `OpenSaml4AuthenticationProvider` 's default authentication converter.
It returns a `Saml2AuthenticatedPrincipal` containing the attributes it extracted from `AttributeStatement` s as well as the single `ROLE_USER` authority. It returns a `Saml2AuthenticatedPrincipal` containing the attributes it extracted from `AttributeStatement` s as well as the single `ROLE_USER` authority.
[[servlet-saml2login-opensamlauthenticationprovider-additionalvalidation]] [[servlet-saml2login-opensamlauthenticationprovider-additionalvalidation]]
==== Performing Additional Validation ==== Performing Additional Validation
`OpenSamlAuthenticationProvider` performs minimal validation on SAML 2.0 Assertions. `OpenSaml4AuthenticationProvider` performs minimal validation on SAML 2.0 Assertions.
After verifying the signature, it will: After verifying the signature, it will:
1. Validate `<AudienceRestriction>` and `<DelegationRestriction>` conditions 1. Validate `<AudienceRestriction>` and `<DelegationRestriction>` conditions
2. Validate `<SubjectConfirmation>` s, expect for any IP address information 2. Validate `<SubjectConfirmation>` s, expect for any IP address information
To perform additional validation, you can configure your own assertion validator that delegates to `OpenSamlAuthenticationProvider` 's default and then performs its own. To perform additional validation, you can configure your own assertion validator that delegates to `OpenSaml4AuthenticationProvider` 's default and then performs its own.
[[servlet-saml2login-opensamlauthenticationprovider-onetimeuse]] [[servlet-saml2login-opensamlauthenticationprovider-onetimeuse]]
For example, you can use OpenSAML's `OneTimeUseConditionValidator` to also validate a `<OneTimeUse>` condition, like so: For example, you can use OpenSAML's `OneTimeUseConditionValidator` to also validate a `<OneTimeUse>` condition, like so:
@ -1269,10 +1269,10 @@ For example, you can use OpenSAML's `OneTimeUseConditionValidator` to also valid
.Java .Java
[source,java,role="primary"] [source,java,role="primary"]
---- ----
OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider();
OneTimeUseConditionValidator validator = ...; OneTimeUseConditionValidator validator = ...;
provider.setAssertionValidator(assertionToken -> { provider.setAssertionValidator(assertionToken -> {
Saml2ResponseValidatorResult result = OpenSamlAuthenticationProvider Saml2ResponseValidatorResult result = OpenSaml4AuthenticationProvider
.createDefaultAssertionValidator() .createDefaultAssertionValidator()
.convert(assertionToken); .convert(assertionToken);
Assertion assertion = assertionToken.getAssertion(); Assertion assertion = assertionToken.getAssertion();
@ -1292,10 +1292,10 @@ provider.setAssertionValidator(assertionToken -> {
.Kotlin .Kotlin
[source,kotlin,role="secondary"] [source,kotlin,role="secondary"]
---- ----
var provider = OpenSamlAuthenticationProvider() var provider = OpenSaml4AuthenticationProvider()
var validator: OneTimeUseConditionValidator = ... var validator: OneTimeUseConditionValidator = ...
provider.setAssertionValidator { assertionToken -> provider.setAssertionValidator { assertionToken ->
val result = OpenSamlAuthenticationProvider val result = OpenSaml4AuthenticationProvider
.createDefaultAssertionValidator() .createDefaultAssertionValidator()
.convert(assertionToken) .convert(assertionToken)
val assertion: Assertion = assertionToken.assertion val assertion: Assertion = assertionToken.assertion
@ -1314,7 +1314,7 @@ provider.setAssertionValidator { assertionToken ->
==== ====
[NOTE] [NOTE]
While recommended, it's not necessary to call `OpenSamlAuthenticationProvider` 's default assertion validator. While recommended, it's not necessary to call `OpenSaml4AuthenticationProvider` 's default assertion validator.
A circumstance where you would skip it would be if you don't need it to check the `<AudienceRestriction>` or the `<SubjectConfirmation>` since you are doing those yourself. A circumstance where you would skip it would be if you don't need it to check the `<AudienceRestriction>` or the `<SubjectConfirmation>` since you are doing those yourself.
[[servlet-saml2login-opensamlauthenticationprovider-decryption]] [[servlet-saml2login-opensamlauthenticationprovider-decryption]]
@ -1322,11 +1322,11 @@ A circumstance where you would skip it would be if you don't need it to check th
Spring Security decrypts `<saml2:EncryptedAssertion>`, `<saml2:EncryptedAttribute>`, and `<saml2:EncryptedID>` elements automatically by using the decryption <<servlet-saml2login-rpr-credentials,`Saml2X509Credential` instances>> registered in the <<servlet-saml2login-relyingpartyregistration,`RelyingPartyRegistration`>>. Spring Security decrypts `<saml2:EncryptedAssertion>`, `<saml2:EncryptedAttribute>`, and `<saml2:EncryptedID>` elements automatically by using the decryption <<servlet-saml2login-rpr-credentials,`Saml2X509Credential` instances>> registered in the <<servlet-saml2login-relyingpartyregistration,`RelyingPartyRegistration`>>.
`OpenSamlAuthenticationProvider` exposes <<servlet-saml2login-architecture,two decryption strategies>>. `OpenSaml4AuthenticationProvider` exposes <<servlet-saml2login-architecture,two decryption strategies>>.
The response decrypter is for decrypting encrypted elements of the `<saml2:Response>`, like `<saml2:EncryptedAssertion>`. The response decrypter is for decrypting encrypted elements of the `<saml2:Response>`, like `<saml2:EncryptedAssertion>`.
The assertion decrypter is for decrypting encrypted elements of the `<saml2:Assertion>`, like `<saml2:EncryptedAttribute>` and `<saml2:EncryptedID>`. The assertion decrypter is for decrypting encrypted elements of the `<saml2:Assertion>`, like `<saml2:EncryptedAttribute>` and `<saml2:EncryptedID>`.
You can replace `OpenSamlAuthenticationProvider`'s default decryption strategy with your own. You can replace `OpenSaml4AuthenticationProvider`'s default decryption strategy with your own.
For example, if you have a separate service that decrypts the assertions in a `<saml2:Response>`, you can use it instead like so: For example, if you have a separate service that decrypts the assertions in a `<saml2:Response>`, you can use it instead like so:
==== ====
@ -1334,7 +1334,7 @@ For example, if you have a separate service that decrypts the assertions in a `<
[source,java,role="primary"] [source,java,role="primary"]
---- ----
MyDecryptionService decryptionService = ...; MyDecryptionService decryptionService = ...;
OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider();
provider.setResponseElementsDecrypter((responseToken) -> decryptionService.decrypt(responseToken.getResponse())); provider.setResponseElementsDecrypter((responseToken) -> decryptionService.decrypt(responseToken.getResponse()));
---- ----
@ -1342,7 +1342,7 @@ provider.setResponseElementsDecrypter((responseToken) -> decryptionService.decry
[source,kotlin,role="secondary"] [source,kotlin,role="secondary"]
---- ----
val decryptionService: MyDecryptionService = ... val decryptionService: MyDecryptionService = ...
val provider = OpenSamlAuthenticationProvider() val provider = OpenSaml4AuthenticationProvider()
provider.setResponseElementsDecrypter { responseToken -> decryptionService.decrypt(responseToken.response) } provider.setResponseElementsDecrypter { responseToken -> decryptionService.decrypt(responseToken.response) }
---- ----
==== ====