I am currently building a Spring Boot web application (2.3.1, but the following issue also was observed with versions 2.1.7 and 2.1.5) that is secured with Spring Security. I mostly use default settings (e.g. embedded Tomcat, embedded H2 database, Spring Web-MVC). I do some custom authentication on a permissive POST
mapping with the following code:
UsernamePasswordAuthenticationToken authentication = ...;
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(authentication);
This works fine. Since I want to use it for student examinations, I want to persist the session with the authentication, so in case the server software would crash or go down for some reason, no one needs to re-authenticate when the server comes up again.
I used Spring Session JDBC for this. I added the following dependency to my pom.xml
:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-jdbc</artifactId>
</dependency>
and the following line to my application.properties
spring.session.store-type=jdbc
This all works fine when I test locally. I see the sessions appearing in my database and everything works great. However, in practice the application is started from an iframe
: a learning management system does an LTI-launch, which is a POST request of which the response ends up in the iframe
. When I attempted to do a test deployment, I got a 403
error that my request was not Authenticated. After a frustrating day of debugging, I ultimately was able to pin down this issue to the fact that adding spring-session-jdbc
to my project causes the web application to send a new session ID cookie on each request of the iframe
. If I repeat the same requests without the iframe
, the same application works fine. If I remove the spring-session-jdbc
dependency, the application works fine within the iframe
as well.
If I perform the authentication within the iframe, I see the following in the Spring Security debug logs (exec-1 is where a succesful authentication happens, exec-2 is the request the iframe performs after being redirected by the exec-1 request):
2020-06-24 22:14:02.015 DEBUG 7474 --- [nio-8031-exec-1] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@671fa7ec
2020-06-24 22:14:02.015 DEBUG 7474 --- [nio-8031-exec-1] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-06-24 22:14:02.063 DEBUG 7474 --- [nio-8031-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2020-06-24 22:14:02.113 DEBUG 7474 --- [nio-8031-exec-2] o.s.security.web.FilterChainProxy : /launch/development at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-06-24 22:14:02.113 DEBUG 7474 --- [nio-8031-exec-2] o.s.security.web.FilterChainProxy : /launch/development at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-06-24 22:14:02.114 DEBUG 7474 --- [nio-8031-exec-2] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2020-06-24 22:14:02.114 DEBUG 7474 --- [nio-8031-exec-2] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
When I perform the same requests with the same running application in a regular browser window, the Spring Security debug logs show the following:
2020-06-24 22:18:10.518 DEBUG 7474 --- [nio-8031-exec-5] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@671fa7ec
2020-06-24 22:18:10.518 DEBUG 7474 --- [nio-8031-exec-5] w.c.HttpSessionSecurityContextRepository : SecurityContext 'org.springframework.security.core.context.SecurityContextImpl@e1239cdc: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@e1239cdc: Principal: testuserid; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_INSTRUCTOR' stored to HttpSession: 'org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper@66670854
2020-06-24 22:18:10.536 DEBUG 7474 --- [nio-8031-exec-5] o.s.s.w.a.ExceptionTranslationFilter : Chain processed normally
2020-06-24 22:18:10.536 DEBUG 7474 --- [nio-8031-exec-5] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2020-06-24 22:18:10.590 DEBUG 7474 --- [nio-8031-exec-6] o.s.security.web.FilterChainProxy : /home at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-06-24 22:18:10.591 DEBUG 7474 --- [nio-8031-exec-6] o.s.security.web.FilterChainProxy : /home at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-06-24 22:18:10.592 DEBUG 7474 --- [nio-8031-exec-6] w.c.HttpSessionSecurityContextRepository : Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: 'org.springframework.security.core.context.SecurityContextImpl@82a4fa0f: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@82a4fa0f: Principal: testuserid; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_INSTRUCTOR'
To me this seems rather odd: if this is purely a browser problem, it should not matter whether I use spring-session-jdbc
. If it is a problem with how spring-session-jdbc
stores my authentication, it should not matter if it happens in an iframe
or not. Is there something I am missing here? Did I stumble on a bug?
I could try a workaround where I use some javascript inside the iframe to let the POST happen in a blank tab, but that feels rather ugly.