Search code examples
spring-securityspring-samlspring-sessionopensamlspring-security-saml2

Spring Saml2 and Spring Session - SavedRequest not retrieved (cannot redirect to requested page after authentication / InResponseTo exception)


I am trying to use Spring Boot SAML2 + Spring Session to secure my web application (to be deployed on K8S). Everything is fine without spring-session-data-rest or spring-session-hazelcast. It can authenticate with Okta and redirect back to the requested page after authentication. Also, I can use either opensaml3 or opensaml4.

However, when I tried to use either spring-session-data-rest or spring-session-hazelcast (just 1 instance, no cluster yet), it would not redirect back to the requested page. Also, it'd fail with opensaml4 with exception: "The response contained an InResponseTo attribute [] but no saved authentication request was found". There's some mentioning about opensaml3 going EOL so I want to make it work with opensaml4.

Here's a sample application to demonstrate my case https://github.com/simonckw/redis-saml2/tree/redis. Have I missed anything? Have anyone got a working sample with this setup? Help is much appreciated.

p.s. I've traced into HttpSessionRequestCache.java, invoked from SavedRequestAwareWarpper.java. Without spring-session-data-rest or spring-session-hazelcast, the saved request can be retrieved but not when either spring-session-data-rest or spring-session-hazelcast is enabled. It also seems to me that the InResponseTo exception could be related too. My Redis setup should be fine. Here's the session data written into Redis:

  1. "spring:session:sessions:expires:7c1858d1-0ea7-4a7a-8523-2abf89137771"
  2. "spring:session:expirations:1654439580000"
  3. "spring:session:sessions:expires:58a584d3-625e-4e0a-bef5-3aaff485ad93"
  4. "spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:[email protected]"
  5. "spring:session:sessions:7c1858d1-0ea7-4a7a-8523-2abf89137771"
  6. "spring:session:sessions:58a584d3-625e-4e0a-bef5-3aaff485ad93"

127.0.0.1:6379> hkeys spring:session:sessions:7c1858d1-0ea7-4a7a-8523-2abf89137771

  1. "sessionAttr:SPRING_SECURITY_SAVED_REQUEST"
  2. "lastAccessedTime"
  3. "maxInactiveInterval"
  4. "creationTime"
  5. "sessionAttr:org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository.SAML2_AUTHN_REQUEST"

127.0.0.1:6379> hkeys spring:session:sessions:58a584d3-625e-4e0a-bef5-3aaff485ad93

  1. "maxInactiveInterval"
  2. "creationTime"
  3. "lastAccessedTime"
  4. "sessionAttr:SPRING_SECURITY_CONTEXT"
  5. "sessionAttr:SPRING_SECURITY_LAST_EXCEPTION"

Solution

  • The problem is that Spring Session is setting SameSite=Lax for its SESSION cookie. Your servlet container isn't setting that on JSESSIONID (when you're not using Spring Session).

    Because the SAML response is being POSTed from Okta's page, the browser will not send the cookie, so Spring doesn't think it has a session in which to find the authentication request. It uses that saved request to reconcile the InResponseTo attribute.

    You can work around this by removing SameSite from the cookie. Create a bean like this:

        @Bean
        public DefaultCookieSerializerCustomizer cookieSerializerCustomizer() {
            return cookieSerializer -> {
                cookieSerializer.setSameSite(null);
            };
        }
    

    Alternatively, you could explicitly specify None, but then you would have to also set the Secure attribute.

    Note: Chrome is supposed to default to Lax where SameSite isn't specified. In reality, it doesn't do that if HttpOnly is set. Safari and Firefox don't even seem to care about HttpOnly.

    This problem is discussed here.