Describe the bug HTTP GraphQL call which includes two internal calls to REQUEST and ASYNC dispatchers. Custom Auth filter extending OncePerRequestFilter execute SecurityContextHolder.getContext().setAuthentication(authentication);
REQUEST dispatcher result produce AuthorizationDecision(granted=true) ASYNC dispatcher produces AuthorizationDecision(granted=false) Due to AnonymousAuthorization
Security Config:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@Slf4j
public class SecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
val authFilter = new MockAuthorizationFilter();
val repository = new RequestAttributeSecurityContextRepository();
http.securityContext((securityContext) -> securityContext
.securityContextRepository(repository)
)
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(config -> {
config.anyRequest().authenticated();
})
.addFilterAfter(authFilter, UsernamePasswordAuthenticationFilter.class);
//WorkAround
/* http.addFilterAfter(new CustomAuthContextFilter(repository), authFilter.getClass());*/
return http.build();
}
}
To Reproduce Run SecurityContextIssueApplication Execute GraphQL call : query DummyQuery { dummyQuery(dummyQueryInput: "12345") }
Expected behavior HTTP status 200 and dummyQuery in GraphQL response: { "data": { "dummyQuery": "DummyGqlResponse" } }
Sample
https://github.com/asndevever/spring-security-auth-context-issue example project
My vision of the problem Bug in SecurityContextHolderFilter
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
if (request.getAttribute(FILTER_APPLIED) != null) {
chain.doFilter(request, response);
return;
}
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
Supplier<SecurityContext> deferredContext = this.securityContextRepository.loadDeferredContext(request);
try {
this.securityContextHolderStrategy.setDeferredContext(deferredContext);
chain.doFilter(request, response);
}
finally {
this.securityContextHolderStrategy.clearContext();
**this.securityContextRepository.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());** //This line is missing
request.removeAttribute(FILTER_APPLIED);
}
}
Compared with the previous implementation in SecurityContextPersistenceFilter.
Workaround: Added to example commented in config Adding this.securityContextRepository.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse()); call into CustomFilter had resolved the issue with Authentication context propagation between Dispatchers.
Comment From: marcusdacoregio
Hi, @asndevever. If you want to save the SecurityContext you have to explicitly call it in your MockAuthorizationFilter implementation, starting from 6.0, Spring Security does not do it automatically anymore. Take a look at the Session Management section of the docs.