DEV Community

loading...
Cover image for Authenticator in Retrofit Android

Authenticator in Retrofit Android

mohitrajput987 profile image Mohit Rajput Updated on ・3 min read

There is a general use-case in any application that the authentication token expires, and you need to refresh the internally. After upgrading the token, the API call should be executed again, and UI should be updated. Let's understand this scenario by an example:

  • You are building a social network application which has various functionalities, i.e. new feeds, friend requests list, send a friend request, see comments on a post etc.
  • After login, you provide an authentication token(i.e. JWT token) to the user. This token has an expiry time of 10 mins(which could be dynamic at server-side), and this should be passed in the header of each API call.
  • When you go to the friend requests list screen, you get 401 Unauthorized which means you need to refresh the token.
  • You not only need to update the token but also make the call to friend requests list API again and show the list in UI.

The naive way of handling this is, on each API call, make a check of 401 status code and call the refresh token API again. After calling the API, make the call to required API again. This code needs to be written everywhere(i.e. in each API call callback).

Does Android(more specific, Retrofit) have any way to handle this scenario using a standard code?
The answer is YES!!! We have a way of handling this scenario in Retrofit using Authenticator.

You can achieve this functionality using simple steps which are described below:

1. Method to fetch updated token

Declare a method in the retrofit interface to fetch the updated token from the server, as shown below:

interface UserApiService {
    companion object {
        private const val REQUEST_REFRESH_TOKEN = "/oauth/token"
    }

    @FormUrlEncoded
    @POST(REQUEST_REFRESH_TOKEN)
    fun getAuthenticationToken(@FieldMap params: HashMap<String, String>): retrofit2.Call<AuthTokenResponse>

    ...
}
Enter fullscreen mode Exit fullscreen mode

2. Create an Authenticator class

Implemented okhttp3.Authenticator interface and override authenticate() method as shown below:

class TokenAuthenticator : Authenticator {
    override fun authenticate(route: Route?, response: Response): Request? {
        // This is a synchronous call
        val updatedToken = getUpdatedToken()
        return response.request().newBuilder()
                .header(ApiClient.HEADER_AUTHORIZATION, updatedToken)
                .build()
    }

    private fun getUpdatedToken(): String {
        val requestParams = HashMap<String, String>()
        ...

        val authTokenResponse = ApiClient.userApiService.getAuthenticationToken(requestParams).execute().body()!!

        val newToken = "${authTokenResponse.tokenType} ${authTokenResponse.accessToken}"
        SharedPreferenceUtils.saveString(Constants.PreferenceKeys.USER_ACCESS_TOKEN, newToken)
        return newToken
    }
}
Enter fullscreen mode Exit fullscreen mode

The authenticate() method is called when server returns 401 Unauthorized. For calling ApiClient.userApiService.getAuthenticationToken(), we're using execute() to make it a synchronous call.

3. Prepare OkHttp client

Now set this TokenAuthenticator in OkHttpClient as shown below:

 OkHttpClient.Builder()
                .connectTimeout(TIMEOUT_IN_SECONDS.toLong(), TimeUnit.SECONDS)
                .readTimeout(TIMEOUT_IN_SECONDS.toLong(), TimeUnit.SECONDS)
                .authenticator(TokenAuthenticator())`
                .addInterceptor(MyInterceptor())
Enter fullscreen mode Exit fullscreen mode

That is it!!!
Now, whenever you make an API call, i.e. friend requests list API call and get 401, the retrofit will call API to refresh the token and make the same request, i.e. friend requests list call again.
You do not need to handle anything on the view layer for this.

Thanks for reading this article. I hope you liked it and understood the solution easily. For any doubts or suggestions, feel free to comment.
If you're new to Kotlin, you can read my previous blog Features You Will Love About Kotlin which will give you the understanding of this beautiful language.

Discussion (6)

pic
Editor guide
Collapse
ashubuntu profile image
ashubuntu

if requestParams potentially consists of username and password, how do you provide that on the fly especially when the primary functionality of the app is to log the user out to the login activity when token is expired. By the way, this technique surely informs about 401 code.

Collapse
mohitrajput987 profile image
Mohit Rajput Author

Ideally we should have a refreshToken() function instead of login again.
But if you want to login again, then you can display a modal to user to enter username and password then resume this functionality as per given in blog.

Collapse
ricindigus profile image
RICARDO MORALES

Awesome, thank you very much!!!!!!!

i need this for my work.
You saved me.

Collapse
mohitrajput987 profile image
Collapse
adriyoutomo profile image
Adri

nice article, i think i should use this in my next project 😁

Collapse
mohitrajput987 profile image
Mohit Rajput Author

Thanks Adri. Sure we should use this.