dgs-framework
dgs-framework copied to clipboard
bug: security context cleared before websocket subscription is initialized
I have previously posted this on Stackoverflow, however I am more and more convinced that this is a bug on DGS therefore I decided to open this issue.
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.
Expected behavior
I expect the @Secured
annotation to work on a @DgsSubscription
method.
Actual behavior
The Security Context is cleared before the websocket for the subscription is initialized.
Versions
graphql-dgs-platform-dependencies: 5.2.4 spring-boot-starter-parent: 2.7.3
Steps to reproduce
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()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.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:
2022-10-25 16:21:54.888 DEBUG 197007 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Securing GET /subscriptions?access_token=[TOKEN_REDACTED]
2022-10-25 16:21:54.892 DEBUG 197007 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2022-10-25 16:21:55.211 DEBUG 197007 --- [nio-8080-exec-1] o.s.s.o.s.r.a.JwtAuthenticationProvider : Authenticated token
2022-10-25 16:21:55.211 DEBUG 197007 --- [nio-8080-exec-1] .o.s.r.w.BearerTokenAuthenticationFilter : Set SecurityContextHolder to JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt@2ba427df, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[ROLE_offline_access, ROLE_uma_authorization, ROLE_default-roles-poc, ROLE_myRole]]
2022-10-25 16:21:55.219 DEBUG 197007 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Secured GET /subscriptions?access_token=[TOKEN_REDACTED]
2022-10-25 16:21:55.252 DEBUG 197007 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
2022-10-25 16:21:55.369 INFO 197007 --- [nio-8080-exec-2] .d.s.w.WebsocketGraphQLWSProtocolHandler : Initialized connection for 65fe0882-1224-d39b-92f3-17eccf63c948
2022-10-25 16:21:55.574 WARN 197007 --- [nio-8080-exec-2] 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]
[...]
Additional info
I deliberately tested this using the WebsocketGraphQLWSProtocolHandler. This one contains code to set the SecurityContext. The WebsocketGraphQLTransportWSProtocolHandler does not contain such code, which makes me wonder if that is a bug by itself.