Android Apollo v3 - Subscriptions with authentication

Hey Folks!

I try to subscribe our Android client with the Apollo client v3.0.0-beta05. For me it is not possible so do a successful subscription. This is how i create the Apollo-Client:

val graphQLWsProtocol = GraphQLWsProtocol.Factory(connectionPayload = mapOf("Authorization" to "Bearer ..."))

ApolloClient
                    .Builder()
                    .httpServerUrl(httpServerUrl = httpServerUrl)
                    .httpEngine(DefaultHttpEngine(get<OkHttpClient>()))
                    .subscriptionNetworkTransport(WebSocketNetworkTransport
                                                          .Builder()
                                                          .serverUrl("...")
                                                          .protocol(graphQLWsProtocol)
                                                          .webSocketEngine(DefaultWebSocketEngine(get<OkHttpClient>()))
                                                          .build())
                    .addCustomScalarAdapter(GraphQLISODateTime.type, DateAdapter())
                    .build()

Is this the right way to add an authentication to wss? My error response is:

Caused by: com.apollographql.apollo3.exception.ApolloWebSocketClosedException: WebSocket Closed code='1002' reason=''
        at com.apollographql.apollo3.network.ws.DefaultWebSocketEngine$open$webSocket$1.onClosing(OkHttpWebSocketEngine.kt:60)
        at okhttp3.internal.ws.RealWebSocket.onReadClose(RealWebSocket.kt:378)
        at okhttp3.internal.ws.WebSocketReader.readControlFrame(WebSocketReader.kt:220)
        at okhttp3.internal.ws.WebSocketReader.processNextFrame(WebSocketReader.kt:104)
        at okhttp3.internal.ws.RealWebSocket.loopReader(RealWebSocket.kt:293)
        at okhttp3.internal.ws.RealWebSocket$connect$1.onResponse(RealWebSocket.kt:195)
        at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:920)

Maybe someone of you have an idea.

Thx!

1 Like

Hi :wave:! That does look appropriate…

Is there any chance your API is public? We could then double check. But if it’s not, I don’t think there’s a standard way to handle authentication so it’s hard to tell.

At this point, I’d recommend double checking the url. If that’s not enough, usually running through a proxy like Charles will highlight the difference and hint at what the problem might be.

Hey Martin,

thanks for your answer! The API is public but need an authentication. I can send you via PM the URL?!

1 Like

I can send you via PM the URL?!

Yep, feel free to send information at martin@apollographql.com

Also, something that just came to mind. Are you sure you need GraphQLWsProtocol? There are 2 protocols:

  • SubscriptionWsProtocol is the default protocol supported by apollo-server
  • GraphQLWsProtocol is a new protocol that can also be used with apollo-server and also others frameworks like express and others.

I’m no expert in the server space but I’d double check which one you need.

Haha, nope. Because we can’t find some documentation, we try some things. I send you the URL via email.

Oh, that looks good! If we use the SubscriptionWsProtocol class. We can subscribe and we get a push into the app! Thanks a lot @mbonnin

val graphQLWsProtocol = SubscriptionWsProtocol.Factory(connectionPayload = {
    mapOf("Authorization" to "Bearer ...")
})
1 Like

Nice :tada:Thanks for the update :blush:

Ok, now i got the subscription and everything works. The SubscriptionWsProtocol is part of the ApolloClient instance and this is a singleton.

But, is there an option to refresh the Token in the SubscriptionWsProtocol? (The Interceptors work only on http and not with ws calls)
What happen if the token is invalid?

Thx & greetings,
Daniel

What happen if the token is invalid?

This is a very good question and the fact that most implementations are server-dependent make it very hard to test against. Not too mention the relative absence of public GraphQL servers supporting subscriptions.

In your specific case, I’d expect that the WebSocket would throw an error and that you would be able to restart it? SubscriptionWsProtocol.connectionPayload is a lambda so you can execute code to refresh your token inside the lambda if needed.

You might need to use the beta06-SNAPSHOT for this as we recently fixed a bug when errors happen.

In all cases, if you ever have a network dump of what happens when the token expires, it’d be awesome if you could share it, we could add that to unit tests.

1 Like