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:
class AuthorizationInterceptor() : HttpInterceptor {private val mutex = Mutex()override suspend fun intercept(request: HttpRequest, chain: HttpInterceptorChain): HttpResponse {var token = mutex.withLock {// get current token}val response = chain.proceed(request.newBuilder().addHeader("Authorization", "Bearer $token").build())return if (response.statusCode == 401) {token = mutex.withLock {// get new token}chain.proceed(request.newBuilder().addHeader("Authorization", "Bearer $token").build())} else {response}}}
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:
val apolloClient = ApolloClient.Builder().httpServerUrl("http://localhost:8080/graphql").webSocketServerUrl("http://localhost:8080/subscriptions").wsProtocol(SubscriptionWsProtocol.Factory(connectionPayload = {// some servers use "authorization" directlymapOf("authorization" to token)// others require to wrap in a "headers" mapmapOf("headers" to mapOf("authorization" to token))// others will expect different maps// refer to your backend docs for the actual map to use})).build()
Alternatively, you can also send headers in the initial WebSocket handshake request:
val apolloClient = ApolloClient.Builder().httpServerUrl("https://example.com/graphql").subscriptionNetworkTransport(WebSocketNetworkTransport.Builder().serverUrl("https://example.com/subscriptions").addHeader("Authorization", authorization).build()).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 will have to force disconnect the WebSocket.
First define your own Exception:
class WebSocketReconnectException: Exception("The WebSocket needs to be reopened")
val apolloClient = ApolloClient.Builder().httpServerUrl("http://localhost:8080/graphql").webSocketServerUrl("http://localhost:8080/subscriptions").wsProtocol(SubscriptionWsProtocol.Factory(connectionPayload = {// the connection_init payload// This is an example. You will most likely have to tweak it for your backendmapOf("Authorization" to token)})).webSocketReopenWhen { e, attempt ->if (e is WebSocketReconnectException) {true} else {// Another WebSocket error happened, decide what to do with it// Here we're trying to reconnect at most 3 timesattempt < 3}}.build()
When you need to reopen the WebSocket, call closeConnection
:
apolloClient.subscriptionNetworkTransport.closeConnection(WebSocketReconnectException())`
The WebSocket will close and reopen with a fresh new connectionPayload
.