Expected Behavior
SAML Single Logout should work even if the principal does not implement the Saml2AuthenticatedPrincipal
interface.
Current Behavior
The following parts of SAML Single Logout only work as expected if the principal implements Saml2AuthenticatedPrincipal
:
- Populating the
NameID
inOpenSamlLogoutRequestResolver
- expects
Authentication.getName()
to return theNameID
value from the SAML Assertion - Populating the
SessionIndex
list inOpenSamlLogoutRequestResolver
- expects the principal to implement
Saml2AuthenticatedPrincipal
- Extracting the
RelyingPartyRegistrationId
from theAuthentication
inOpenSamlLogoutRequestResolver
,OpenSamlLogoutResponseResolver
andSaml2LogoutRequestFilter
- expects the principal to implement
Saml2AuthenticatedPrincipal
- if it does not, the
RelyingPartyRegistrationId
must be in the request URL, at least when usingDefaultRelyingPartyRegistrationResolver
- there is some duplicate code in these three classes
- Request matching for
LogoutFilter
inSaml2LogoutConfigurer.Saml2RequestMatcher
- expects the principal to implement
Saml2AuthenticatedPrincipal
All these don't work if the responseAuthenticationConverter
in OpenSaml(4)AuthenticationProvider
is customized and the principal does not implement Saml2AuthenticatedPrincipal
.
It would be great if SAML Single Logout could be configured to work with any principal. This could be achieved using yet another resolver interface - passing HttpServletRequest
and Authentication
as parameters should be sufficient. The implementations of this resolver interface could then be made to match the responseAuthenticationConverter
.
Context
In some cases, this issue can be solved by making the principal implement Saml2AuthenticatedPrincipal
. Unfortunately, this does not work in my specific case, because the principal is created and used in code that doesn't use Spring Security.
Comment From: jzheaux
Hi, @chschu, thanks for the suggestions.
It would be great if SAML Single Logout could be configured to work with any principal. This could be achieved using yet another resolver interface - passing HttpServletRequest and Authentication as parameters should be sufficient.
Isn't this what Saml2LogoutResponseResolver
already is? You could be able to provide your own implementation of this.
In some cases, this issue can be solved by making the principal implement Saml2AuthenticatedPrincipal. Unfortunately, this does not work in my specific case, because the principal is created and used in code that doesn't use Spring Security.
I'm not seeing why this is a problem. Can you clarify why you can't provide a custom responseAuthenticationConverter
that constructs an adapter that extends your custom datatype and implements Saml2AuthenticatedPrincipal
?
Comment From: chschu
Thanks for your quick response, @jzheaux.
TL;DR: It's all solvable, but the suggested resolver would simplify things a lot.
Isn't this what
Saml2LogoutResponseResolver
already is? You could be able to provide your own implementation of this.
You are right, and that's what I'm currently doing. It all depends on what the responseAuthenticationConverter
stuffs into the Authentication
.
The full "workaround" involves creating a Saml2Authentication
(using the default converter created by OpenSaml4AuthenticationProvider.createDefaultResponseAuthenticationConverter()
) and putting it inside my Authentication
. That happens in my custom responseAuthenticationConverter
in the authentication process.
During SLO, my custom Saml2LogoutRequestResolver
/Saml2LogoutResponseResolver
extract the Saml2Authentication
from my Authentication
and delegate to OpenSaml4LogoutRequestResolver
/OpenSaml4LogoutResponseResolver
. These generate exactly what I need, as long as I pass them the Saml2Authentication
.
The request matching for the LogoutFilter
was solvable using an ObjectPostProcessor
that creates the LogoutFilter
from scratch. That's because I need to use a RequestMatcher
that is consistent with the one in LogoutConfigurer
. Unlike the LogoutSuccessHandler
and the LogoutHandler
s, the RequestMatcher
is not currently taken from there by Saml2LogoutConfigurer
.
I'm not seeing why this is a problem. Can you clarify why you can't provide a custom
responseAuthenticationConverter
that constructs an adapter that extends your custom datatype and implementsSaml2AuthenticatedPrincipal
?
That was my first thought as well, and it works as long as you know what the principal actually is. Unfortunately, I do not know the runtime type of the principal. I just get that thing from one service, and I have to pass it to other services later on. So while I could easily subclass the declared type of the principal, subclassing the runtime type is a whole different story.
Comment From: jzheaux
the suggested resolver would simplify things a lot
I'm happy to take a look at a PR to clear up any ambiguity with what you are proposing. That said, I'm hesitant to merge a new interface that has the same contract as Saml2LogoutResponseResolver
.
Unfortunately, I do not know the runtime type of the principal. I just get that thing from one service, and I have to pass it to other services later on. So while I could easily subclass the declared type of the principal, subclassing the runtime type is a whole different story.
Why don't you know the runtime type? If it returns an interface with multiple implementations, you could create some kind of delegate class that implements this interface and Saml2AuthenticatedPrincipal
. You could add a getDelegate
if getting the concrete instance is important. Or perhaps you can have multiple adapters, one for each concrete implementation and do an instanceof
check.
Otherwise, I think your wrapping solution sounds quite reasonable. This is ultimately the adapter approach I mentioned, just with the extra flexibility that knowing the type is not needed.
LogoutFilter
If you want to pursue the idea of simplifying how SAML Logout and generic logout get the same request matcher, I'd be happy to discuss. Would you open a new ticket to track that since it probably affects more than just those who are wanting to not use Saml2AuthenticatedPrincipal
?
Comment From: chschu
Created #10821 for the request matcher part.
I'll try to provide a PR for the resolver part.
Comment From: chschu
Sorry for the delay - I've been quite busy the last few weeks.
I'm planning to implement a Converter<Authentication, Optional<Saml2AuthenticatedPrincipal>>
with a default implementation that returns authentication.getPrincipal()
if it is a Saml2AuthenticatedPrincipal
.
A custom implementation of the converter can then create/obtain the Saml2AuthenticatedPrincipal
however it is possible. In my specific case I could extract it from the Saml2Authentication
I'm already storing in my own Authentication
.
Comment From: chschu
Eventually, I chose a less intrusive approach that solves my issue - see PR #11338.
Implementing a Spring Security interface on the Authentication
is a lot easier than somehow extending the authenticated principal Object
in my specific case.
Comment From: OrangeDog
Extending your own principal object to implement Saml2AuthenticatedPrincipal
can indeed be an issue, especially if it's a JPA entity.
Comment From: jzheaux
Closing in favor of https://github.com/spring-projects/spring-security/pull/11338