dgs-framework icon indicating copy to clipboard operation
dgs-framework copied to clipboard

feature: Add Bearer auth token support to subscriptions

Open metsfan opened this issue 3 years ago • 16 comments

Describe the Feature Request

Right now it is not possible to have bearer authorization on a DGS subscription. Therefore if your subscription requires user authorization in order to succeed, it will not be possible.

Describe Preferred Solution

Allow some methodology for providing an authorization interceptor on the DGS subscription websocket

Describe Alternatives

Allow library clients to provide an alternative bean for the websocket handler.

metsfan avatar Jun 28 '21 13:06 metsfan

Websockets don't have HTTP headers, which is why you can't use regular headers for this.

At Netflix we use a filter that basically takes the bearer out of a URL parameter. Since this isn't really standard, I'm not sure what the framework can/should do.

paulbakker avatar Jun 28 '21 17:06 paulbakker

So the Apollo clients allow you to provide a "payload" on the initial websocket connection. In the GraphQL kickstart library you can define a bean which extends ApolloSubscriptionConnectionListener and allows you to read from the initialization payload and update the Spring security context. See a code sample below from my project (which currently uses GraphQL kickstart).

If we could get something like this in DGS, it would be very helpful.

However I am interested in how you are doing this in Netflix. How do you access this URL parameter? And is there any way to execute this as a filter so I can still use the @PreAuthorize annotation on my @DgsSubscription method? Sorry if this is an obvious question. Will a regular filter be executed when the websocket connects?

EDIT - Ok I see waht you mean now, I created a new filter for the websocket connection and I see now how you mean you're extracting it in this way. This is a bit different from how Apollo iOS client wants you to authenticate but I suppose I'll go with it for now instead of asking you guys to change the library. I do think being able to extract the Apollo init payload would be nice. But I understand that there is a workaround so I'll go with that for now.

@Component
class SubscriptionConnectionListener(
    private val tokenAuthorizationFilter: TokenAuthorizationFilter,
    private val authenticator: TokenBasedAuthenticator
) : ApolloSubscriptionConnectionListener {
    override fun onConnect(session: SubscriptionSession?, message: OperationMessage?) {
        super.onConnect(session, message)

        (message?.payload as? Map<*, *>)?.let { payload ->
            payload[AuthorizationHeader]?.toString()?.let {
                session?.userProperties?.put(AuthorizationHeader, it)
            }
        }
    }

    override fun onStart(session: SubscriptionSession?, message: OperationMessage?) {
        super.onStart(session, message)

        session?.userProperties?.get(AuthorizationHeader)?.toString()?.let { authorization ->
            tokenAuthorizationFilter.readToken(authorization)?.let { token ->
                authenticator.authenticateUser(token)
            }
        }
    }
}

metsfan avatar Jun 28 '21 18:06 metsfan

This is what I mean btw. As you can see during the Subscription initialization, I've passed through a payload from the IOS client which has the Authorization header. All we need here is a listener here that passes through the payload. This is basically the "proper" way to authenticate clients with a websocket. Screen Shot 2021-06-28 at 2 10 17 PM

metsfan avatar Jun 28 '21 18:06 metsfan

@paulbakker I am going to implement this for my application. If I make a pull request do you think this is something you would consider incorporation?

metsfan avatar Jun 28 '21 23:06 metsfan

Yes, definitely interested. It hasn't been an issue for us because we already had the authz filters available for Spring Boot, but ideally, it should just work out of the box with Apollo.

paulbakker avatar Jun 29 '21 17:06 paulbakker

Spring Security supports WebSocket, but not sure if it is simple to add extra authentication to the Spring SecurityContextHodler on the server-side automatically.

hantsy avatar Aug 15 '21 09:08 hantsy

https://www.apollographql.com/docs/graphql-subscriptions/authentication/ https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_connection_init

amondnet avatar Aug 16 '21 17:08 amondnet

The ApolloGraphQL subscription websocket protocol defines a connection init event which can be used to security context handling, it is better to add callbacks to all of these events, thus the handling can be customized.

Vertx Web GraphQL allows developers to add callbacks as expected.

hantsy avatar Aug 28 '21 09:08 hantsy

Hi there, is there any update on this? @paulbakker you were saying you use a filter at Netflix? How are you doing that exactly?

Did anyone succeed building something that makes the security context available when connecting?

Any help is very much appreciated!

LexanRed avatar Jan 23 '22 16:01 LexanRed

Sorry to bother you again, did anyone succeed in building a filter which adds the security context at least during the handshake request?

LexanRed avatar Feb 08 '22 17:02 LexanRed

@LexanRed I achieved this but I had to make pretty significant changes to the default DGS websocket module. If you dont wnat to modify the module, the easiest workaround is to send the bearer token as a parameter into your subscription call and then modify the security context in your @DgsSubscription method. If you want to use @PreAuthorize, just add the annotation to another method which returns your Publisher. . It's a workaround for sure but it's not really a big deal imo since this really isn't any different from whats happening with the headers.

aeskreis-ta avatar Feb 08 '22 20:02 aeskreis-ta

@aeskreis-ta Thanks for your advice! That sounds like a practical and nice solution. Generally editing libraries brings always hassle when new versions are released since the changes have to be merged manually. Therefore I really like your approach.

Would you mind posting an example here? That would be a big help!

I'd be very interested in the changes you had to make as well as the more practical solution you mentioned.

LexanRed avatar Feb 13 '22 10:02 LexanRed

The problem I see with adding @PreAuthorize is exceptions are not handled as with queries or mutations. At that point in time, the websocket session is already established.

What I imagine : The session is not established, if no token is sent or if the token is invalid.

2022-02-13 13:18:11.027 DEBUG 39866 --- [nio-8080-exec-6] c.c.f.c.IpAddressInterceptor             : Request from 127.0.0.1
2022-02-13 13:18:11.253  INFO 39866 --- [nio-8080-exec-8] c.n.g.d.s.w.DgsWebSocketHandler          : Initialized connection for ec1e6b0e-0b7f-40da-de53-0f7edd7e60e9
2022-02-13 13:18:11.276  WARN 39866 --- [nio-8080-exec-8] n.g.e.SimpleDataFetcherExceptionHandler  : Exception while fetching data (/subscribeToMessages) : 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:333) ~[spring-security-core-5.5.3.jar:5.5.3]
	at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:200) ~[spring-security-core-5.5.3.jar:5.5.3]
	at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:58) ~[spring-security-core-5.5.3.jar:5.5.3]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.12.jar:5.3.12]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.12.jar:5.3.12]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.12.jar:5.3.12]
	at com.coduction.famulex.messaging.graph.MessagingEndpoint$$EnhancerBySpringCGLIB$$3017023b.subscribeToMessages(<generated>) ~[main/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:282) ~[spring-core-5.3.12.jar:5.3.12]
	at com.netflix.graphql.dgs.internal.DataFetcherInvoker.invokeDataFetcher(DataFetcherInvoker.kt:121) ~[graphql-dgs-4.9.20.jar:4.9.20]
	at com.netflix.graphql.dgs.internal.DgsSchemaProvider.createBasicDataFetcher$lambda-19(DgsSchemaProvider.kt:321) ~[graphql-dgs-4.9.20.jar:4.9.20]
	at graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentation.lambda$instrumentDataFetcher$0(DataLoaderDispatcherInstrumentation.java:87) ~[graphql-java-17.3.jar:na]
	at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:279) ~[graphql-java-17.3.jar:na]
	at graphql.execution.SubscriptionExecutionStrategy.createSourceEventStream(SubscriptionExecutionStrategy.java:91) ~[graphql-java-17.3.jar:na]
	at graphql.execution.SubscriptionExecutionStrategy.execute(SubscriptionExecutionStrategy.java:53) ~[graphql-java-17.3.jar:na]
	at graphql.execution.Execution.executeOperation(Execution.java:159) ~[graphql-java-17.3.jar:na]
	at graphql.execution.Execution.execute(Execution.java:105) ~[graphql-java-17.3.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:613) ~[graphql-java-17.3.jar:na]
	at graphql.GraphQL.parseValidateAndExecute(GraphQL.java:538) ~[graphql-java-17.3.jar:na]
	at graphql.GraphQL.executeAsync(GraphQL.java:502) ~[graphql-java-17.3.jar:na]
	at com.netflix.graphql.dgs.internal.BaseDgsQueryExecutor.baseExecute(BaseDgsQueryExecutor.kt:119) ~[graphql-dgs-4.9.20.jar:4.9.20]
	at com.netflix.graphql.dgs.internal.DefaultDgsQueryExecutor.execute(DefaultDgsQueryExecutor.kt:75) ~[graphql-dgs-4.9.20.jar:4.9.20]
	at com.netflix.graphql.dgs.DgsQueryExecutor.execute(DgsQueryExecutor.java:54) ~[graphql-dgs-4.9.20.jar:4.9.20]
	at com.netflix.graphql.dgs.subscriptions.websockets.DgsWebSocketHandler.handleSubscription(DgsWebSocketHandler.kt:108) ~[graphql-dgs-subscriptions-websockets-4.9.20.jar:4.9.20]
	at com.netflix.graphql.dgs.subscriptions.websockets.DgsWebSocketHandler.handleTextMessage(DgsWebSocketHandler.kt:75) ~[graphql-dgs-subscriptions-websockets-4.9.20.jar:4.9.20]
	at org.springframework.web.socket.handler.AbstractWebSocketHandler.handleMessage(AbstractWebSocketHandler.java:43) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:58) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:114) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$000(StandardWebSocketHandlerAdapter.java:43) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:85) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:82) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:415) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.sendMessageText(WsFrameServer.java:129) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:515) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:301) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:85) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:183) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:162) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:157) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:60) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1722) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]

2022-02-13 13:18:11.277 ERROR 39866 --- [nio-8080-exec-8] w.s.h.ExceptionWebSocketHandlerDecorator : Closing session due to exception for StandardWebSocketSession[id=ec1e6b0e-0b7f-40da-de53-0f7edd7e60e9, uri=ws://localhost:8080/subscriptions]

java.lang.NullPointerException: executionResult.getData() must not be null
	at com.netflix.graphql.dgs.subscriptions.websockets.DgsWebSocketHandler.handleSubscription(DgsWebSocketHandler.kt:109) ~[graphql-dgs-subscriptions-websockets-4.9.20.jar:4.9.20]
	at com.netflix.graphql.dgs.subscriptions.websockets.DgsWebSocketHandler.handleTextMessage(DgsWebSocketHandler.kt:75) ~[graphql-dgs-subscriptions-websockets-4.9.20.jar:4.9.20]
	at org.springframework.web.socket.handler.AbstractWebSocketHandler.handleMessage(AbstractWebSocketHandler.java:43) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:58) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:114) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$000(StandardWebSocketHandlerAdapter.java:43) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:85) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:82) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:415) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.sendMessageText(WsFrameServer.java:129) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:515) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:301) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:85) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:183) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:162) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:157) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:60) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1722) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]

2022-02-13 13:18:12.990  INFO 39866 --- [        Timer-0] c.n.g.d.s.w.DgsWebSocketHandler          : Cleaning up for session ec1e6b0e-0b7f-40da-de53-0f7edd7e60e9

As you see, the connection first gets initialized, then the security check happens and then it is cancelled by the DgsWebsocketHandler. But not because of security. It's cancelled beacuse the result must not be null.

Maybe @paulbakker can give us a hint?

LexanRed avatar Feb 13 '22 12:02 LexanRed

Another problem is, that the SecurityContext is sometimes there and sometimes not.

That's the stacktrace i get after simply reloading the page. As you can see, now I get "Access is denied", before it was "An Authentication object was not found in the SecurityContext".

Adding a filter, which reliably adds the SecurityContext would be preferable.

2022-02-13 13:23:37.110  WARN 39866 --- [nio-8080-exec-1] n.g.e.SimpleDataFetcherExceptionHandler  : Exception while fetching data (/subscribeToMessages) : Access is denied

org.springframework.security.access.AccessDeniedException: Access is denied
	at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:73) ~[spring-security-core-5.5.3.jar:5.5.3]
	at org.springframework.security.access.intercept.AbstractSecurityInterceptor.attemptAuthorization(AbstractSecurityInterceptor.java:238) ~[spring-security-core-5.5.3.jar:5.5.3]
	at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:208) ~[spring-security-core-5.5.3.jar:5.5.3]
	at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:58) ~[spring-security-core-5.5.3.jar:5.5.3]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.12.jar:5.3.12]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.12.jar:5.3.12]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.12.jar:5.3.12]
	at com.coduction.famulex.messaging.graph.MessagingEndpoint$$EnhancerBySpringCGLIB$$3017023b.subscribeToMessages(<generated>) ~[main/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:282) ~[spring-core-5.3.12.jar:5.3.12]
	at com.netflix.graphql.dgs.internal.DataFetcherInvoker.invokeDataFetcher(DataFetcherInvoker.kt:121) ~[graphql-dgs-4.9.20.jar:4.9.20]
	at com.netflix.graphql.dgs.internal.DgsSchemaProvider.createBasicDataFetcher$lambda-19(DgsSchemaProvider.kt:321) ~[graphql-dgs-4.9.20.jar:4.9.20]
	at graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentation.lambda$instrumentDataFetcher$0(DataLoaderDispatcherInstrumentation.java:87) ~[graphql-java-17.3.jar:na]
	at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:279) ~[graphql-java-17.3.jar:na]
	at graphql.execution.SubscriptionExecutionStrategy.createSourceEventStream(SubscriptionExecutionStrategy.java:91) ~[graphql-java-17.3.jar:na]
	at graphql.execution.SubscriptionExecutionStrategy.execute(SubscriptionExecutionStrategy.java:53) ~[graphql-java-17.3.jar:na]
	at graphql.execution.Execution.executeOperation(Execution.java:159) ~[graphql-java-17.3.jar:na]
	at graphql.execution.Execution.execute(Execution.java:105) ~[graphql-java-17.3.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:613) ~[graphql-java-17.3.jar:na]
	at graphql.GraphQL.parseValidateAndExecute(GraphQL.java:538) ~[graphql-java-17.3.jar:na]
	at graphql.GraphQL.executeAsync(GraphQL.java:502) ~[graphql-java-17.3.jar:na]
	at com.netflix.graphql.dgs.internal.BaseDgsQueryExecutor.baseExecute(BaseDgsQueryExecutor.kt:119) ~[graphql-dgs-4.9.20.jar:4.9.20]
	at com.netflix.graphql.dgs.internal.DefaultDgsQueryExecutor.execute(DefaultDgsQueryExecutor.kt:75) ~[graphql-dgs-4.9.20.jar:4.9.20]
	at com.netflix.graphql.dgs.DgsQueryExecutor.execute(DgsQueryExecutor.java:54) ~[graphql-dgs-4.9.20.jar:4.9.20]
	at com.netflix.graphql.dgs.subscriptions.websockets.DgsWebSocketHandler.handleSubscription(DgsWebSocketHandler.kt:108) ~[graphql-dgs-subscriptions-websockets-4.9.20.jar:4.9.20]
	at com.netflix.graphql.dgs.subscriptions.websockets.DgsWebSocketHandler.handleTextMessage(DgsWebSocketHandler.kt:75) ~[graphql-dgs-subscriptions-websockets-4.9.20.jar:4.9.20]
	at org.springframework.web.socket.handler.AbstractWebSocketHandler.handleMessage(AbstractWebSocketHandler.java:43) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:58) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:114) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$000(StandardWebSocketHandlerAdapter.java:43) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:85) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:82) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:415) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.sendMessageText(WsFrameServer.java:129) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:515) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:301) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:85) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:183) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:162) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:157) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:60) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1722) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]

2022-02-13 13:23:37.111 ERROR 39866 --- [nio-8080-exec-1] w.s.h.ExceptionWebSocketHandlerDecorator : Closing session due to exception for StandardWebSocketSession[id=224a553e-8690-4f85-21ad-82a7bd485217, uri=ws://localhost:8080/subscriptions]

java.lang.NullPointerException: executionResult.getData() must not be null
	at com.netflix.graphql.dgs.subscriptions.websockets.DgsWebSocketHandler.handleSubscription(DgsWebSocketHandler.kt:109) ~[graphql-dgs-subscriptions-websockets-4.9.20.jar:4.9.20]
	at com.netflix.graphql.dgs.subscriptions.websockets.DgsWebSocketHandler.handleTextMessage(DgsWebSocketHandler.kt:75) ~[graphql-dgs-subscriptions-websockets-4.9.20.jar:4.9.20]
	at org.springframework.web.socket.handler.AbstractWebSocketHandler.handleMessage(AbstractWebSocketHandler.java:43) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:58) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:114) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$000(StandardWebSocketHandlerAdapter.java:43) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:85) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:82) ~[spring-websocket-5.3.12.jar:5.3.12]
	at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:415) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.sendMessageText(WsFrameServer.java:129) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:515) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:301) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:85) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:183) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:162) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:157) ~[tomcat-embed-websocket-9.0.54.jar:9.0.54]
	at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:60) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1722) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.54.jar:9.0.54]
	at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]

2022-02-13 13:23:37.990  INFO 39866 --- [        Timer-0] c.n.g.d.s.w.DgsWebSocketHandler          : Cleaning up for session 224a553e-8690-4f85-21ad-82a7bd485217

LexanRed avatar Feb 13 '22 12:02 LexanRed

In the above linked ticket there is an explained workaround to work with HTTP Headers from the handshake request. I do think however we should have a way to use the connection_init message instead. SO IMO there is the bug that is mentioned above and a feature is more about adding support for connection_init based authz. Or should I create another feature request ?

lthoulon-locala avatar Sep 25 '23 07:09 lthoulon-locala

Hi @lthoulon-locala - This is not a high priority use case for us since we don't use webflux stack internally. We will try to address this when we can, but we do accept contributions in the meantime if anyone is interested in doing so.

srinivasankavitha avatar Sep 25 '23 17:09 srinivasankavitha