Problems with the context in Graphql VertxDataFetcher Environment

151 views
Skip to first unread message

Aleksandar Pavlovic

unread,
Mar 16, 2020, 5:17:29 AM3/16/20
to vert.x
Hi,

my current project is using vertx-web-graphql module.

I have a real problem with authentication and authorization during subscription process. I know that the problem with authentication and authorization with WebSocket is well known.
Anyway, I've found that during the first call, Reactjs Apollo client sends a message with authToken. This message could be captured inside message handler in the following way:
val apolloWSHandler = ApolloWSHandler
     
.create(graphQL)
     
.messageHandler {
       
if (it.type() == ApolloWSMessageType.CONNECTION_INIT) {
          println
(it.content())

       
}
     
}

This way I can filter for token and validate user and that could be one way of blocking unwanted request.

Unfortunatelly, that doesn't solve the authorization problem inside data fetchers. DataFetcher doesn't have anyting in common with ApolloWSHandler.
I didn't find a way to pass some values from ApolloWSHandler handlers to Graphql context. For some reason I thought that will be done out of box :-(

Specialized type of DataFetcher is VertxDataFetcher that receives DataFetchingEnrironment and Promise as method arguments during execution.
I found very easy implementation for Query and Mutation over Graphql and HTTP. That wasn't the case with Apollo WebSocket implementation.

The main difference between HTTP and WS is that method getContext() on environment object returns:
  • RoutingContext when use HTTP ( as expected )
  • Unit ( Void ) when use WS for subscription
I know that WebSocket doesn't have RoutingContext during execution, all messages are part of one connection in multiplex world, but I hoped that it would be possible to have some kind of session management available inside environment.

DataFetcher for Query over HTTP:
VertxDataFetcher<String> { env, prom ->
  val context
= env.getContext<RoutingContext>()
  context
.request().headers().forEach(::println)
  prom
.complete("Hello From Graphql")
}

DataFetcher for Subscription over WebSocket:
VertxDataFetcher<String> { env, prom ->
 
// env.getContext() returns Unit
 
// in this moment I need some user reference
  prom
.complete("Hello From Graphql")
}

I didn't find any documentation about this kind of usage. Maybe I'm missing something on the concept level.
Only one solution that could give me required information for subscription authorization is passing another token inside payload but honestly I think that some kind of session management should be much more efficient and secure.

Any suggestion is welcome


Thomas SEGISMONT

unread,
Mar 16, 2020, 5:59:28 AM3/16/20
to vert.x
Hi,

For authentication, I would recommend to use io.vertx.ext.web.handler.graphql.ApolloWSHandler#connectionHandler
This handler will be called at the beginning of each websocket connection, so you can use it together with io.vertx.core.http.ServerWebSocket#setHandshake to verify the token provided.

To provide your data fetchers with some context, you can use io.vertx.ext.web.handler.graphql.ApolloWSHandler#queryContext
By default, no context is set (unlike the HTTP GraphQL handler which uses the routing context) but you can set your own object

--
You received this message because you are subscribed to the Google Groups "vert.x" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vertx+un...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/vertx/0830f314-c4b8-453a-9cb6-7e7cd0b2a709%40googlegroups.com.

M B

unread,
Mar 16, 2020, 12:20:56 PM3/16/20
to vert.x
Thank you for your fast reply. We are having hard time finding proper docs.
       
Playing with the queryContext we can achieve to find it in the environment inside the data fetcher, but it gets created at each message.
It does not work as a session persisting across messages.

E.g., in the following code, we have:

val apolloWSHandler = ApolloWSHandler.create(graphQL)
       
.queryContext {
           
MyAppContext.getMock()
       
}


fun fetchUser
(): VertxDataFetcher<User> {
   
return VertxDataFetcher { env, promise ->
       
GlobalScope.launch(vertx.dispatcher()) {
           
try {
                val ctx
= env.getContext<MyAppContext>()
                println
("UUid :  ${ctx.uuid}")
               
....
                promise
.complete(user)
           
} catch (e: Throwable) {
                promise
.fail(e)
           
}
       
}
   
}
}


And the uuid, which is a random value, is always different.

How can we achieve to have a persistent context across multiple message, to store for example user data?
What are we missing? Is there any specific docs we can look into?    

Thank you for your patience.
To unsubscribe from this group and stop receiving emails from it, send an email to ve...@googlegroups.com.

Dimitris Zenios

unread,
Mar 16, 2020, 12:28:19 PM3/16/20
to vert.x
What you need is a way to persist data in connection handler that can be accessed in query context factory.

With http request you have the routing context already providing the user through routing context

To unsubscribe from this group and stop receiving emails from it, send an email to vertx+un...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/vertx/e75c75dc-99d9-49fe-b0da-247654225b65%40googlegroups.com.

M B

unread,
Mar 16, 2020, 3:14:37 PM3/16/20
to vert.x
Thank you for your reply.

Do you have an example with code?
Sorry, i am missing something, but i cannot uderstand how to provide the same context with every message of the same websocket connection.
Is this feature absent in vertx and do I have to implement my own logic to retrieve the same object, or there is something already build it?

Thank you

Dimitris Zenios

unread,
Mar 16, 2020, 3:54:24 PM3/16/20
to vert.x
//Pseudo code

Map<String,Strong> map = new HashMap();

connectionHandler((socket) -> {
map.put(socket.textHandlerId(),"My Context");
})

queryContext((message) => {
return map.get(message.serverWebSocket().textHandlerId())
})





To unsubscribe from this group and stop receiving emails from it, send an email to vertx+un...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/vertx/ae370e0c-7a09-4e14-b218-caab47a6436c%40googlegroups.com.

Aleksandar Pavlovic

unread,
Mar 16, 2020, 5:22:22 PM3/16/20
to vert.x
Thanks for clarification.

Your pseudo code shows how it can be done in the very simple way.
As my colleague sad, we didn't find any documentation about websocket session management topic.
IMO, this could be nice pattern for securing websocket sessions in general.

Dimitris Zenios

unread,
Mar 16, 2020, 5:38:03 PM3/16/20
to vert.x
To tell you the truth i had the same issues and in the end i forked the whole subscription implementation to add what is needed.

The two problems i faced with the current implementation are

1. There is no predefined way to pass data from connection handler down to query or query context (http has the routing context)

2. Any query handler should wait for the connection handler to succeed / fail before pushing the query to the graphql engine.Right now if you do a promise request in connection handler, query handler does not wait for it to complete hence there is no way to be sure that until the query handler is called my user is authorized or not.the original apollo implementation does exactly this.


To unsubscribe from this group and stop receiving emails from it, send an email to vertx+un...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/vertx/2deb4260-f9ff-4179-a0c4-0eb9e97eec13%40googlegroups.com.

Dimitris Zenios

unread,
Mar 16, 2020, 5:39:01 PM3/16/20
to vert.x
Some other missing things are headers and other data from upgrade request.

Ip, locale etc

Thomas SEGISMONT

unread,
Mar 17, 2020, 6:25:32 AM3/17/20
to vert.x
Thanks for sharing your tips here. The Vert.x Web GraphQL module is a recent addition to the stack, if you have proposals for code and documentation, pull requests are very much welcome.

Reply all
Reply to author
Forward
0 new messages