Authenticate your operations


Authentication is not included in the GraphQL specification. This page aims at giving some guidance for the most common scenarios but doesn't intend to be exhaustive.

Authenticating your HTTP requests with OkHttp

OkHttp Interceptors are an easy way to add an "Authorization" header to your HTTP requests.

OkHttp Interceptors have been around for a long time and work well but can only be used with Apollo Kotlin on Android and the JVM.

Authenticating your HTTP requests with Apollo HttpInterceptor

In order to authenticate your HTTP requests in a multi-platform way, you can use an Apollo HttpInterceptor.

HttpInterceptor is multiplatform and uses an API very similar to OkHttp's:

Kotlin
1class AuthorizationInterceptor() : HttpInterceptor {
2  private val mutex = Mutex()
3
4  override suspend fun intercept(request: HttpRequest, chain: HttpInterceptorChain): HttpResponse {
5    var token = mutex.withLock {
6      // get current token
7    }
8
9    val response = chain.proceed(request.newBuilder().addHeader("Authorization", "Bearer $token").build())
10
11    return if (response.statusCode == 401) {
12      token = mutex.withLock {
13        // get new token
14      }
15      chain.proceed(request.newBuilder().addHeader("Authorization", "Bearer $token").build())
16    } else {
17      response
18    }
19  }
20}

For a more advanced example, you can take a look at the AuthorizationInterceptor integration tests

Authenticating your WebSockets

Authenticating WebSockets is typically handled with a specific connection payload:

Kotlin
1val apolloClient = ApolloClient.Builder()
2    .httpServerUrl("http://localhost:8080/graphql")
3    .webSocketServerUrl("http://localhost:8080/subscriptions")
4    .wsProtocol(
5        SubscriptionWsProtocol.Factory(
6            connectionPayload = {
7              // some servers use "authorization" directly
8              mapOf("authorization" to token)
9              // others require to wrap in a "headers" map
10              mapOf("headers" to mapOf("authorization" to token))
11              // others will expect different maps
12              // refer to your backend docs for the actual map to use
13            }
14        )
15    )
16    .build()
17

Alternatively, you can also send headers in the initial WebSocket handshake request:

Kotlin
1val apolloClient = ApolloClient.Builder()
2    .httpServerUrl("https://example.com/graphql")
3    .subscriptionNetworkTransport(
4        WebSocketNetworkTransport.Builder()
5            .serverUrl("https://example.com/subscriptions")
6            .addHeader("Authorization", authorization)
7            .build()
8    )
9    .build()

Updating your WebSocket token

When using authenticated subscriptions over WebSockets, you might want to refresh your authentication token because it was invalidated or simply because it expired.

 note
If your user logged out, you might want to consider creating a new ApolloClient instead. This will make sure that all other state besides the WebSocket, like cache for an example, is new and not shared amongst different users.

Sometimes the websocket will emit an error that you can use in webSocketReopenWhen to create a new WebSocket. Other times that information will come from elsewhere in your app, and you have to force disconnect the WebSocket.

First define your own Exception:

Kotlin
1class WebSocketReconnectException: Exception("The WebSocket needs to be reopened")
Kotlin
1    val apolloClient = ApolloClient.Builder()
2        .httpServerUrl("http://localhost:8080/graphql")
3        .webSocketServerUrl("http://localhost:8080/subscriptions")
4        .wsProtocol(
5            SubscriptionWsProtocol.Factory(
6                connectionPayload = {
7                  // the connection_init payload
8                  // This is an example. You will most likely have to tweak it for your backend
9                  mapOf("Authorization" to token)
10                }
11            )
12        )
13        .webSocketReopenWhen { e, attempt ->
14          if (e is WebSocketReconnectException) {
15            true
16          } else {
17            // Another WebSocket error happened, decide what to do with it
18            // Here we're trying to reconnect at most 3 times
19            attempt < 3
20          }
21        }
22        .build()

When you need to reopen the WebSocket, call closeConnection:

Kotlin
1apolloClient.subscriptionNetworkTransport.closeConnection(WebSocketReconnectException())`

The WebSocket will close and reopen with a fresh new connectionPayload.

Feedback

Edit on GitHub

Forums