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:
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:
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.
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 }:
1ApolloClient.Builder()
2 .serverUrl(mockServer.url())
3 .retryOnError { it.operation is Subscription }For other cases, customize your RetryStrategy. For details, see Network errors.