GraphQL every day becomes more popular as the backend API choice so it’s natural that Android Devs needs to deal with it more often. Apollo is commonly used as the GraphQL client on the mobile side that makes it easier to manage the communication but there are always some use cases that we need to handle by ourselves. One of those cases is refreshing access token expiration with the refresh token for GraphQL APIs using Apollo and this is what this article is about. If you are interested only in code and you want to analyze it by yourself, you can go straight to the final code section.
Note: this article was based on the Apollo 2.4.0 version, Apollo API may be changed in future versions.
- A request is being rejected with an Unauthorized error message.
- Use a refresh token to gain a new access token.
- Retry sending the request with a new access token.
Using Apollo Interceptor
First of all, we will need to create and add Apollo Interceptor to our Apollo Client instance. If you are already familiar with REST API token handling, this concept should be already well-known to you. Let’s see the docs:
“ApolloInterceptor is responsible for observing and modifying the requests going out and the corresponding responses coming back in.”
Sounds promising for us! Let’s create a class that implements an interceptor interface and override both `interceptAsync` and `dispose` functions. We will also need here our tokens storage and class that will manage our refresh request. In my case, I will pass them by constructor:
Now you can call `addApplicationInterceptor` directly on Apollo Client builder and pass freshly created class.
Go back inside your interceptor. You are overriding two functions, let’s focus on the `interceptAsync`, we will cover `dispose` later. First of all, we will use the chain to proceed with the ongoing request:
We need to pass three arguments here, `request: ApolloInterceptor.InterceptorRequest`, `dispatcher: Executor`, `callback: ApolloInterceptor.CallBack`.
Basically, we need to add our authorization header to the request that is going out of our app.
We simply pass the already received dispatcher here.
Finally, we reached our refreshing token handling. In this callback, we will handle the response, here we will check if it was successful and if it wasn’t, we will handle the reauthentication process. Let’s implement ApolloInterceptor.CallBack!
All arguments passed to that method are the same as for `interceptAsync`. So for fetching and failure events we are not doing anything special. It’s up to you but in this case we are simply pushing the events upstream and on complete, we won’t do anything with it. Everything will be covered inside the `onResponse` method. To catch only unauthorized response error here we added `isUnauthenticated` and its implementation will be based on your backend response, e.g. you will check if in response errors there’s some tag or code.
If it’s true, then you need to handle re-authentication. Here you need to use your refresh token to ask your authentication service for the new access token. When you successfully receive a new token, then you proceed with the request again. Otherwise, you need to propagate the response up, so you will handle it by the upper layer, e.g. by logging out the user. Example code with rxJava but this method will be based on your implementation.
That’s simply it! Important note: be aware of all the callback invocations, don’t miss them!
Be careful with dealing with the `dispose` method, especially when you will have singleton instance of the ApolloClient cause this will be called even when any error occurs, and if you add any real disposing here, you may not be able to make any other requests in the application lifetime. So I suggest leaving it empty or dispose only temporary objects(e.g. clear your rx disposables). This can be tricky so if you want to post any code here, be sure to deeply debug it, especially in the failure case.
You can also add a retry counter for the reauthentication to prevent making too many calls. To do that, firstly declare a var int field, globally in the class. I suggest you adding `@Volatile` annotation to it, to make sure that the counter will be refreshed on all threads. You could also define const max retries count in your companion object but it’s up to you.
There will be several places where the counter will be called, please check the final code section to see where it appears.
The final code
Let’s sum up the code that we wrote. You need to adjust it by your Token storage implementation and refresh token call. Be aware of the callback invocations!