Netflix / dgs-framework

GraphQL for Java with Spring Boot made easy.
https://netflix.github.io/dgs
Apache License 2.0
3.06k stars 295 forks source link

feature: Add Bearer auth token support to subscriptions #450

Closed metsfan closed 1 month ago

metsfan commented 3 years ago

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.

paulbakker commented 3 years ago

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.

metsfan commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

@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?

paulbakker commented 3 years ago

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.

hantsy commented 3 years ago

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

amondnet commented 3 years ago

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

hantsy commented 3 years ago

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.

LexanRed commented 2 years ago

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 commented 2 years ago

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

aeskreis-ta commented 2 years ago

@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.

LexanRed commented 2 years ago

@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 commented 2 years ago

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 commented 2 years ago

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
lthoulon-locala commented 12 months ago

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 ?

srinivasankavitha commented 12 months ago

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.

paulbakker commented 1 month ago

Closing this as this is now handled by Spring GraphQL with the DGS/Spring GraphQL integration.

inverseTrig commented 2 weeks ago

Closing this as this is now handled by Spring GraphQL with the DGS/Spring GraphQL integration.

@paulbakker Could you please elaborate on how this is now handled? It would be great if you could point me to some resource so I could implement this in my project. Thanks in advance!

paulbakker commented 2 weeks ago

I'm not sure about the specifics of Bearer auth, but these are the transport docs for Spring GraphQL: https://docs.spring.io/spring-graphql/reference/transports.html.

What I meant by "this is now handled..." I meant the transports in general, which means we don't have that in the DGS code base any more.