How to update websocket connection http header for existing kotlin apollo client?

I have a necessity to attach authorization header to websocket connections (subscriptions) in my Android application. So from time to time auth token becomes expired and needs to be refreshed.
Currently I create apollo client like this:

	ApolloClient.Builder()
		.serverUrl(BuildConfig.DATA_BASE_URL)
		.subscriptionNetworkTransport(
			WebSocketNetworkTransport.Builder()
				.serverUrl(BuildConfig.DATA_BASE_URL)
				.addHeader(AUTHORIZATION, "$BEARER $accessToken")
				.build()
		)
		.build()

Is there any way to update the token when it should be refreshed?

Hi :wave:

Is there any way to update the token when it should be refreshed?

There are but since WebSockets are not part of the specs, there are sadly multiple ways to do so.

The main question is how do you know when the token expires? Do you get a GraphQL error from your server? Or a WebSocket error? Or something else?

The main question is how do you know when the token expires? Do you get a GraphQL error from your server? Or a WebSocket error? Or something else?

Yes, if a token expire, our gateway redirects the request to auth page, so I receive 1002 WebSocket protocol error.

So I wonder if it is possible to change the header in, for example, the reopenWhen block?

Sadly there is no such thing yet. You can only update the url and/or the connection_init params. Do you mind opening an issue? I’ll look into adding this for the experimental websockets.

Turns out I lied, the experimental websockets already support this :tada:

I just added some more docs: Implement ApolloWebSocketClosedException on darwin targets and update docs by martinbonnin · Pull Request #6275 · apollographql/apollo-kotlin · GitHub

The tldr; is to use retryOnErrorInterceptor() with a custom interceptor:

private object RetryException : Exception()

private class RetryOnErrorInterceptor : ApolloInterceptor {
    override fun <D : Operation.Data> intercept(request: ApolloRequest<D>, chain: ApolloInterceptorChain): Flow<ApolloResponse<D>> {
      var attempt = 0
      return flow {
        val request =  request.newBuilder()
                .addHttpHeader("Authorization", "Bearer ${accessToken()}")
                .build()
    
        emitAll(chain.proceed(request))
      }.onEach {
        when (val exception = it.exception) {
          is ApolloWebSocketClosedException -> {
            if (exception.code == 1002 && exception.reason == "unauthorized") {
              attempt = 0 // retry immediately
              throw RetryException
            }
          }
          is ApolloNetworkException -> {
            // Retry all network exceptions
            throw RetryException
          }
          else -> {
            // Terminate the subscription
          }
        }
      }.retryWhen { cause, _ ->
        cause is RetryException
      }
    }
}