Global/Single Logout with SAML
Hi,
We have a SAML authentication system up and running using the spring-security-saml module. It uses a checkout from around August 2011 and so I’m afraid it hasn’t got a version number.
We are encountering a problem when people logout from our system, and they are not logged out from their IDP. This causes no problems until 2 hours after their initial login at which point if they try to login to our system using SAML they get the following:-
HTTP ERROR: 401
Authentication Failed: Users authentication data is too old
This continues for another 6 hours until the IDP session times out (at 8 hours) or until they restart their browser which kills off the IDP session.
To fix this I am thinking that we need to change our system so that when they logout they are also logged out from the IDP. I think this is called Single Logout or Global Logout, and in our case it would be SP initiated. I'm going to tackle IDP initiated Single Logout (if its possible) later.
I have 3 questions regarding this:-
1. The documentation (from 2009, which was updated 5 months ago) which is at:-
https://github.com/SpringSource/spri...umentation.pdf
says "The SAML Single log-out profile is currently not supported, invocation of logout filter at /saml/logout context will only destroy the local session.". Its possible that between 2009 and when we downloaded the module in August 2011 this functionality was implemented. In the code we have the org.springframework.security.saml.SAMLLogoutFilter has the comment:-
/**
* Logout filter leveraging SAML 2.0 Single Logout profile. Upon invocation of the filter URL it is
* determined whether global (termination of all participating sessions) or local (termination of only
* session running within Spring Security) logout is requested based on request attribute.
* <p/>
* In case global logout is in question a LogoutRequest is sent to the IDP.
*
* @author Vladimir Schäfer
*/
So from this I'm thinking that /saml/logout should now try to logout the user from the IDP. Is that correct?
2. I've been debugging org.springframework.security.saml.SAMLLogoutFilter .doFilterHttp and have found that it is not doing the global logout because the Authentication object that it gets from:-
Authentication auth = SecurityContextHolder.getContext().getAuthenticati on();
is an object of class org.springframework.security.providers.ExpiringUse rnameAuthenticationToken which has a null value in its Object credentials. This causes isGlobalLogout to return false, which stops the global logout code from executing.
I looked for places where ExpiringUsernameAuthenticationToken is created and found it being constructed at org.springframework.security.saml.SAMLAuthenticati onProvider.authenticate(Authentication authentication) at:-
ExpiringUsernameAuthenticationToken result = new ExpiringUsernameAuthenticationToken(expiration, principal, credential, entitlements);
Debugging this showed that the credential in this object (which had id 13760) was set to the credential used in login.
But debugging:-
Authentication auth = SecurityContextHolder.getContext().getAuthenticati on();
when I do a logout shows that the same object (id 13760) now has a null value, so it has been erased by something.
The superclass of ExpiringUsernameAuthenticationToken is UsernamePasswordAuthenticationToken which I see has a eraseCredentials() method. Putting a breakpoint there I find that during login
org.springframework.security.saml.SAMLProcessingFi lter.attemptAuthentication() calls:-
return getAuthenticationManager().authenticate(token);
which calls:-
org.springframework.security.authentication.Provid erManager.authenticate(Authentication authentication)
which has the following bit of code in:-
if (result != null) {
if (eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
// Authentication is complete. Remove credentials and other secret data from authentication
((CredentialsContainer)result).eraseCredentials();
}
The comment at the top of org.springframework.security.authentication.Provid erManager says:-
* Post-authentication, the credentials will be cleared from the returned {@code Authentication} object, if it
* implements the {@link CredentialsContainer} interface. This behaviour can be controlled by modifying the
* {@link #setEraseCredentialsAfterAuthentication(boolean) eraseCredentialsAfterAuthentication} property.
Searching google for "spring saml erase credentials" I find:-
http://forum.springsource.org/showth...g-Security-3-1
which says:-
A few people have been asking how to run Spring SAML with Spring Security 3.1. This is pretty easy, you need to:
Change references to GrantedAuthority to ? extends GrantedAuthority
Update the securityContext.xml to use the new <security:http security="none"> format
If you want to use the sample example as is you will also need to use erase-credentials=false
I've put together a patched version of the saml2-core which runs against Spring Security 3.1 and also updated the saml2-sample app so it also works with 3.1. You can download it here (see the "Others" section). I'll contact the spring saml team and see if they want these changes feeding into the project
Cheers
It is possible that our SAML code was written and tested before an upgrade of Spring Security Core was done, and when it was upgraded to spring.security.core-3.1.0 that we are now using the Global Logout stopped working (we didn't notice because we don't have any live users yet).
Doing more searching for "authentication-manager and eraseCredentialsAfterAuthentication" I found:-
https://issues.jasig.org/browse/CAS-...story-tabpanel
which is about a bug with "CAS Server" where the following isn't working:-
<sec:authentication-manager alias="casAuthenticationManager" erase-credentials="false">
So I looked in our security xml config and found:-
<security:authentication-manager alias="authenticationManager">
to which I added the erase-credentials="false" string:-
<security:authentication-manager alias="authenticationManager" erase-credentials="false">
and now it all works. Debugging shows that the eraseCredentials() method is no longer being called, and when I logout from our system it also logs out from the IDP server and then comes back and logs out from our server.
So, finally, to question number 2. Is this the right way of getting single logout to work and are there any security implications I should be worried about now we are keeping the users credentials in memory for the whole of the session (24 hours), when without the erase-credentials="false" setting they would automatically be erased?
3. If we did upgrade to Spring Security 3.1 since the original SAML code was written should I be worrying about changing the other 2 things mentioned in the Post above i.e. "Change references to GrantedAuthority to ? extends GrantedAuthority" and "Update the securityContext.xml to use the new <security:http security="none"> format" ?
I have also downloaded the latest version of the code+sample+docs and am going to read the new doc and follow the instructions to get it working with Global Logout as its says "Pressing global logout will destroy both local session and session at IDP."
Thanks in advance for your help.
Steve