I am working on a Graphql API and want to authenticate and authorize Graphql requests. I have the setup working just fine for queries/mutations. However, with subscriptions I am running into some issues.
Versions
graphql-dgs-platform-dependencies: 5.2.4
spring-boot-starter-parent: 2.7.3
I have this datafetcher:
@DgsComponent
@RequiredArgsConstructor
public class StocksDataFetcher implements SupportsGlobalObjectIdentification {
private final StocksService service;
@DgsSubscription
@Secured("ROLE_myRole")
public Publisher<Stock> stocks(DataFetchingEnvironment dfe) {
final var sctx = SecurityContextHolder.getContext();
return service.getStocksPublisher();
}
}
This SecurityFilter chain:
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
final var resolver = new DefaultBearerTokenResolver();
resolver.setAllowUriQueryParameter(true);
http.cors()
.and().securityContext().securityContextRepository(new NullSecurityContextRepository())
.and()
.authorizeHttpRequests().anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.bearerTokenResolver(resolver)
.jwt()
;
return http.build();
}
}
I have setup the graphql client to pass the jwt token as access_token query parameter. I get the following logs:
o.s.security.web.FilterChainProxy : Securing GET /subscriptions?access_token=[JWT REDACTED]
s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
o.s.s.o.s.r.a.JwtAuthenticationProvider : Authenticated token
.o.s.r.w.BearerTokenAuthenticationFilter : Set SecurityContextHolder to JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt@b3e4fcc6, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[SCOPE_profile, SCOPE_email]]
o.s.security.web.FilterChainProxy : Secured GET /subscriptions?access_token=[JWT REDACTED]
s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
bsocketGraphQLTransportWSProtocolHandler : Initialized connection for 56ec6e0e-406d-b970-7a16-46fd9ff2da03
n.g.e.SimpleDataFetcherExceptionHandler : Exception while fetching data (/stocks) : An Authentication object was not found in the SecurityContext
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:336) ~[spring-security-core-5.7.3.jar:5.7.3]
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:200) ~[spring-security-core-5.7.3.jar:5.7.3]
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:58) ~[spring-security-core-5.7.3.jar:5.7.3]
[...]
Looking at the logs we can see that the request is authenticated, but it seems that the SecurityContextHolder is cleared before the method security on the data fetcher is evaluated.
Any suggestions on how to configure this correctly or as to what is going on here, is very much appreciated.