You are viewing documentation for a preview version of this software.

Learn about previews.

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

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

Authenticate your subscriptions with an HTTP header or query parameter

ApolloClient creates different WebSockets for different header values. Use an interceptor to change the header value on error:

Kotlin
1private class UpdateAuthorizationHeaderInterceptor : ApolloInterceptor {
2  @OptIn(ExperimentalCoroutinesApi::class)
3  override fun <D : Operation.Data> intercept(request: ApolloRequest<D>, chain: ApolloInterceptorChain): Flow<ApolloResponse<D>> {
4    return flow {
5      // Retrieve a new access token every time
6      val request = if (request.operation is Subscription<*>) {
7        request.newBuilder()
8            // Update the HTTP header
9            .addHttpHeader("Authorization", "Bearer ${accessToken()}")
10            // Or change the url
11            .url("https://example.com/subscription?token=${accessToken()}")
12            .build()
13      } else {
14        request
15      }
16
17      emitAll(chain.proceed(request))
18    }
19  }
20}

Note: The client shares WebSockets in a pool. If your access token changes frequently, the client might open multiple WebSockets concurrently.

Authenticate your subscriptions with a connection payload

The connection payload is specific to the WsProtocol. Pass a lambda to the WsProtocol constructor that the client evaluates whenever it creates a WebSocket.

Kotlin
1ApolloClient.Builder()
2  .httpServerUrl(mockServer.url())
3  .subscriptionNetworkTransport(
4      WebSocketNetworkTransport.Builder()
5          .serverUrl(mockServer.url())
6          .wsProtocol(GraphQLWsProtocol(
7              connectionPayload = {
8                getFreshConnectionPayload()
9              }
10          ))
11          .build()
12  )
13  .build()

Retry your subscriptions automatically

The preceding approaches require your backend to indicate when a token expires. Unlike HTTP 401 errors, WebSockets lack a widespread standard for expiration signaling.

If your backend closes the WebSocket, retry subscriptions automatically using retryOnError { it.operation is Subscription }:

Kotlin
1ApolloClient.Builder()
2  .serverUrl(mockServer.url())
3  .retryOnError { it.operation is Subscription }

For other cases, customize your RetryStrategy. For details, see Network errors.

Feedback

Edit on GitHub

Ask Community