Rework Saml2 Authentication Statement
This commit separates the authentication principal, the assertion details, and the relying party tenant into separate components. This allows the principal to be completely decoupled from how Spring Security triggers and processes SLO. Specifically, it adds Saml2AssertionAuthentication, a new authentication implementation that allows an Object principal and a Saml2ResponseAssertionAccessor credential. It also moves the relying party registration id from Saml2AuthenticatedPrincipal to Saml2AssertionAuthentication. As such, Saml2AuthenticatedPrincipal is now deprecated in favor of placing its assertion components in Saml2ResponseAssertionAccessor and the relying party registration id in Saml2AssertionAuthentication. Closes gh-10820
This commit is contained in:
@@ -54,3 +54,57 @@ fun logoutResponseResolver(registrations: RelyingPartyRegistrationRepository?):
|
||||
----
|
||||
======
|
||||
|
||||
== Favor `Saml2ResponseAuthenticationAccessor` over `Saml2AuthenticatedPrincipal`
|
||||
|
||||
Spring Security 7 separates `<saml2:Assertion>` details from the principal.
|
||||
This allows Spring Security to retrieve needed assertion details to perform Single Logout.
|
||||
|
||||
This deprecates `Saml2AuthenticatedPrincipal`.
|
||||
You no longer need to implement it to use `Saml2Authentication`.
|
||||
|
||||
Instead, the credential implements `Saml2ResponseAssertionAccessor`, which Spring Security 7 favors when determining the appropriate action based on the authentication.
|
||||
|
||||
This change is made automatically for you when using the defaults.
|
||||
|
||||
If this causes you trouble when upgrading, you can publish a custom `ResponseAuhenticationConverter` to return a `Saml2Authentication` instead of returning a `Saml2AssertionAuthentication` like so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
OpenSaml5AuthenticationProvider authenticationProvider() {
|
||||
OpenSaml5AuthenticationProvider authenticationProvider =
|
||||
new OpenSaml5AuthenticationProvider();
|
||||
ResponseAuthenticationConverter defaults = new ResponseAuthenticationConverter();
|
||||
authenticationProvider.setResponseAuthenticationConverter(
|
||||
defaults.andThen((authentication) -> new Saml2Authentication(
|
||||
authentication.getPrincipal(),
|
||||
authentication.getSaml2Response(),
|
||||
authentication.getAuthorities())));
|
||||
return authenticationProvider;
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun authenticationProvider(): OpenSaml5AuthenticationProvider {
|
||||
val authenticationProvider = OpenSaml5AuthenticationProvider()
|
||||
val defaults = ResponseAuthenticationConverter()
|
||||
authenticationProvider.setResponseAuthenticationConverter(
|
||||
defaults.andThen { authentication ->
|
||||
Saml2Authentication(authentication.getPrincipal(),
|
||||
authentication.getSaml2Response(),
|
||||
authentication.getAuthorities())
|
||||
})
|
||||
return authenticationProvider
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
If you are constructing a `Saml2Authentication` instance yourself, consider changing to `Saml2AssertionAuthentication` to get the same benefit as the current default.
|
||||
|
||||
@@ -341,8 +341,10 @@ class MyUserDetailsResponseAuthenticationConverter implements Converter<Response
|
||||
Saml2Authentication authentication = this.delegate.convert(responseToken); <1>
|
||||
UserDetails principal = this.userDetailsService.loadByUsername(username); <2>
|
||||
String saml2Response = authentication.getSaml2Response();
|
||||
Saml2ResponseAssertionAccessor assertion = new OpenSamlResponseAssertionAccessor(
|
||||
saml2Response, CollectionUtils.getFirst(response.getAssertions()));
|
||||
Collection<GrantedAuthority> authorities = principal.getAuthorities();
|
||||
return new Saml2Authentication((AuthenticatedPrincipal) userDetails, saml2Response, authorities); <3>
|
||||
return new Saml2AssertionAuthentication(userDetails, assertion, authorities); <3>
|
||||
}
|
||||
|
||||
}
|
||||
@@ -361,8 +363,10 @@ open class MyUserDetailsResponseAuthenticationConverter(val delegate: ResponseAu
|
||||
val authentication = this.delegate.convert(responseToken) <1>
|
||||
val principal = this.userDetailsService.loadByUsername(username) <2>
|
||||
val saml2Response = authentication.getSaml2Response()
|
||||
val assertion = OpenSamlResponseAssertionAccessor(
|
||||
saml2Response, CollectionUtils.getFirst(response.getAssertions()))
|
||||
val authorities = principal.getAuthorities()
|
||||
return Saml2Authentication(userDetails as AuthenticatedPrincipal, saml2Response, authorities) <3>
|
||||
return Saml2AssertionAuthentication(userDetails, assertion, authorities) <3>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user