Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guidance about websocket authentication & authentication update #2702

Closed
benoitletondor opened this issue Oct 26, 2020 · 11 comments · Fixed by #2706
Closed

Guidance about websocket authentication & authentication update #2702

benoitletondor opened this issue Oct 26, 2020 · 11 comments · Fixed by #2706

Comments

@benoitletondor
Copy link

benoitletondor commented Oct 26, 2020

Question.

The documentation describes in a clear way how to handle authentication for http requests but it's not clear (at least for me) how to handle it for websockets and most importantly, how to update it at runtime (in case of a token refresh).

So I have 2 questions:

  • How to handle authentication with websockets?
  • How to handle authentication changes at runtime with websockets? On iOS, the ApolloWebSocket exposes a method called updateHeaderValues that allow to update authentication headers, does this library have a similar mechanism?

Many thanks in advance.

@benoitletondor benoitletondor changed the title Guidance about websocket authentication update Guidance about websocket authentication & authentication update Oct 26, 2020
@martinbonnin
Copy link
Contributor

Hi 👋 ! Thanks for opening this! I'm pretty new to the WebSocket part of the lib so take this with a grain of salt but I'd say one way to do it would be to implement your own WebSocketSubscriptionTransport ? And have it reconnect when the token needs to be refreshed?

Out of curiosity, how do you know you need to refresh the token? Do you receive an error message from the server or is this initiated client side with a timeout?

@benoitletondor
Copy link
Author

Well the idea was to avoid recoding a whole WebSocketSubscriptionTransport but that's always an option yes. The part that could be complicated is having the websocket reconnect without closing the subscriptions.

I'm pretty new to the Android Apollo library so I'm not really sure but having implemented it on iOS I renew my auth token if I get an authentication error during a query/mutation or if a websocket get an authentication error. When done, I update the websocket headers (using the updateHeaderValues method) and it reconnects to the server with the new auth.

I guess it's a pretty standard way of handling token auth as token can have a short lifespan so you would expect them to be refreshed at runtime.

@martinbonnin
Copy link
Contributor

Can you try using something like this:

okHttpClient.addInterceptor(/* the same interceptor you used for queries/mutations */)

// Manually reconnect when a new token is required

apolloClient.subscriptionManager.stop()
apolloClient.subscriptionManager.start()

@benoitletondor
Copy link
Author

I already saw that but the code looks like this:

  /**
   * Unsubscribe from all active subscriptions, and disconnect the web socket.
   */
  void stop();

which makes me think that subscriptions will be killed when doing that

@martinbonnin
Copy link
Contributor

which makes me think that subscriptions will be killed when doing that

Mmm right, it does look like the SubscriptionManager removes everything. I'll try to see if I can make a SubscriptionManager.reconnect() that keeps all subscriptions active.

@martinbonnin
Copy link
Contributor

@benoitletondor
Tentative fix there: #2706.
Example usage there

I'm still unclear whether authentication should be in the WebSocket request or the connection params? It looks like both solutions are possible?

In all cases, apolloClient.subscriptionManager.reconnect() will reconnect the websocket while keeping the subscriptions. Let me know what you think.

@benoitletondor
Copy link
Author

Hey @martinbonnin ,

Many thanks for that, looks great!

I'm still unclear whether authentication should be in the WebSocket request or the connection params? It looks like both solutions are possible?

I'm not sure I got what you mean here?

@martinbonnin
Copy link
Contributor

I'm not sure I got what you mean here?

We have:

          // goes into a "connection_init" websocket message
          ApolloClient.builder().subscriptionConnectionParams {
            SubscriptionConnectionParams(mapOf("token" to accessToken))
          }

And

          // Will add the token as part of the initial WebSocket request
          OkHttpClient.Builder().addNetworkInterceptor { chain ->
            val request = chain.request().newBuilder().addHeader("token", accessToken).build()
            chain.proceed(request)
          }

I guess you're using the second one?

@benoitletondor
Copy link
Author

Indeed, the second one. But I don't see the difference in what we want to achieve which is reconnecting to the websocket when token changes at runtime, no matter the method used we would need that reconnect mechanism? Maybe I'm missing something, sorry if that's the case

@martinbonnin
Copy link
Contributor

no matter the method used we would need that reconnect mechanism

Yes. A better way to do this would certainly be to have a custom websocket message. That'd save having to tear down the web socket and renegociate it again. Something like update_connection_param. But yea, something for the longer term. In the short term, reconnect seems to be the way to go.

@martinbonnin
Copy link
Contributor

Hi everyone 👋 , I know it's been a long time but if anyone comes here, we've adding some documentation about this at #4128

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants