-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HttpSessionSecurityContextRepository fails to create a session because of the deferred security context support #12314
Comments
Hello @sbegaudeau! Thank you for the detailed write up. I have responded below.
I am unable to reproduce this with a simple sample application. You can find directions for setup in the latest commit. Can you update the sample to reproduce the issue you are seeing?
I'm able to reproduce this error and believe that Start by creating a new
Then in your security configuration add:
If that fixes the second issue, then we can get a fix pushed to Spring Security. However, I'd like to understand the first error as well. |
Thanks for the response, I have tested your
Well, I have looked into it since it seems strange now that I think about it. I have deactivated the In my application, I am relying on spring-graphql and from what I can see, it appears that everything is working nicely on the spring-security side but once I'm inside spring-graphql there seems to be a call to From what I understand, after the successful authentication using OAuth2, my frontend is redirecting the user to the homepage
It seems that the first GraphQL request will always work and the second one will always fail so my frontend always end up receiving a 401 and thus it redirects my user on the login page. Between the two, Spring GraphQL always changes the value in the SecurityContextHolder. So I suspect that by using Would that be possible? |
I have the same issue with this and acting weird.
I have set BUT, if run step 4 run before step 2, everything works fine. Full repo: https://github.com/oldshensheep/shiny-octo-memory/tree/spring-security-issue-12314 @Configuration
@RestController
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
System.out.println("DemoApplication started");
}
private SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository();
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests((authz) -> authz
.anyRequest().permitAll())
.securityContext((context) -> context
.requireExplicitSave(false))
.formLogin().disable();
return http.build();
}
@GetMapping("/who")
public String getName() {
return SecurityContextHolder.getContext().getAuthentication().getName();
}
@GetMapping("/login")
public String login(HttpServletRequest request, HttpServletResponse response) {
var context = SecurityContextHolder.createEmptyContext();
var authentication = new UsernamePasswordAuthenticationToken(UUID.randomUUID(), "password");
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
securityContextRepository.saveContext(context, request, response);
return SecurityContextHolder.getContext().getAuthentication().getName();
}
@GetMapping("/login-not-working")
public String login2(HttpServletRequest request, HttpServletResponse response) {
var context = SecurityContextHolder.createEmptyContext();
var authentication = new UsernamePasswordAuthenticationToken(UUID.randomUUID(), "password");
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
// securityContextRepository.saveContext(context, request, response);
return SecurityContextHolder.getContext().getAuthentication().getName();
}
} |
Hi @oldshensheep, have you tried Rob's workaround? |
@marcusdacoregio |
Hi @sbegaudeau, I was able to simulate your scenario using GraphQL, and the problem has been fixed via this issue #11962. The problem is that Spring GraphQL handles the request asynchronously, making use of the ASYNC dispatch, and the Can you upgrade the version and tell us if that fixes your problem? |
Hi, Sorry for the delay, I had to find some time to update my application but I've switched to Spring Boot 3.0.5 and everything is working as expected. Thanks for the fix 👍 |
Describe the bug
Short version
The new support for deferred security context makes
DelegatingSecurityContextRepository#loadContext
callHttpSessionSecurityContextRepository#loadDeferredContext
whileHttpSessionSecurityContextRepository#loadContext
would have been called before. The response wrapper is thus not configured byHttpSessionSecurityContextRepository#loadContext
when the context is loaded but only when the context is to be saved. As a result, when that wrapper is finally created, the response is now committed, it did not have the ability to act as anOnCommittedResponseWrapper
and it can't create a session anymore, it's too late.Long version
I am using the
oauth2Login()
in my security configuration and there seems to be a change in behavior caused by this commit introducing the support for deferred SecurityContext which makes my application throw an exception with Spring Security 6.0.0 while it worked with Spring Security 5.7.3.I am using the OAuth2 support to log in with Github and everything works fine with regard to the communication with Github but by migrating to Spring Security 6.0.0, after the authentication, new requests cannot find the security context and return a 401.
I have thus used
http.securityContext((securityContext) -> securityContext.requireExplicitSave(false));
to restore the existing behavior of Spring Security 5.7 but now the code is producing the following error:From what I have understood, in my application with Spring Security 5.7.3, the
SecurityContextPersistenceFilter
used theHttpSessionSecurityContextRepository
and the call toSecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);
would thus callHttpSessionSecurityContextRepository#loadContext
. In that method, a wrapper was added to both the request and the response:As a result, when the success handler of my OAuth2 login was called, it could perform the redirection with:
Now the problem is that, this call to
sendRedirect
is executed by anOnCommittedResponseWrapper
, which is theHeaderWriterResponse
(as before) and underneath I don't have theSaveToSessionResponseWrapper
anymore. In Spring Security 5.7, this wrapper would save the context by executing the following code before the response was committed:Now instead, the call to
HttpSessionSecurityContextRepository#loadDeferredContext
caused byDelegatingSecurityContextRepository#loadContext
does not create this wrapper anymore. A wrapper is only created later thanks to the call to:By the
SecurityContextPersistenceFilter
after the response has been committed. Now the methodDelegatingSecurityContextRepository#saveContext
will callHttpSessionSecurityContextRepository#saveContext
which will create a new response wrapper but long after the response has been committed:When this new instance of the response wrapper will try to do its job, the response would have been committed already and the exception would appear. As a result, the authentication does not work.
Expected behavior
The use of
HttpSessionSecurityContextRepository
should create the same wrapper as before to save the security context in the session before the response is committed. I don't know ifHttpSessionSecurityContextRepository#loadDeferredContext
can do it since we don't have access to the response since it is "lost" by theDelegatingSecurityContextRepository
in:The text was updated successfully, but these errors were encountered: