1

We are using the Netflix DGS framework to build our backend to provide a GraphQL API.

In addition to that we use Keykloak as an identity provider which comes with a handy Spring module to add support for authentication and authorization out of the box.

Every request contains a JWT token, which gets validated and from there a SecurityContext object is being generated which is then available in every endpoint.

This is working great for HTTP requests. GraphQL queries and mutations are sent via HTTP, therefore no problem here.

Subscriptions on the other hand use the web socket protocol. A WS request does not contain additional headers, therefore no JWT token is sent with the request.

We can add the token via a payload, the question is now how to set up a Spring Security Filter which creates a Security Context out of the payload.

I guess this is rather Spring specific, basically a filter which intercepts any web socket request (ws://... or wss://...) is needed.

Any help or hint is very much appreciated!

lexan_red
  • 21
  • 3
  • Hi @lexan_red I found this after creating a similar question. I concluded that the only feasible approach would be to use a query string parameter to pass the token. Unfortunately I run into a different issue, but at least a security context is created. See here: https://stackoverflow.com/questions/74138263/how-to-authorize-subscriptions-with-dgs-and-spring-security Hope this helps. – André Oct 20 '22 at 13:53

1 Answers1

1

The only way to use headers in web socket messages is in the connection_init message. the headers will be sent by the client in the payload of the message.

The solution I propose is done in 2 steps (We will assume that the name of the header element is "token"):

  1. Intercept the connection_init message, then force the insertion of a new element (token) in the subscription request.
  2. Retrieve the element (token) of the header during the interception of the subscription and feed the context.

Concretely, the solution is the implementation of WebSocketGraphQlInterceptor interface

@Configuration
class SubscriptionInterceptor implements WebSocketGraphQlInterceptor {
    
    @Override
    public Mono<Object> handleConnectionInitialization(WebSocketSessionInfo sessionInfo, Map<String, Object> connectionInitPayload) {
        sessionInfo.getHeaders().add("token", connectionInitPayload.get("token").toString());
        return Mono.just(connectionInitPayload);
    }
    
    @Override
    public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
        List<String> token = request.getHeaders().getOrEmpty("token");
        return chain.next(request).contextWrite(context -> context. Put("token", token.isEmpty() ? "" : token.get(0)));
    }
}