I want to refresh my access token when it gets expired. I have implemented Authenticator as shown below:
@Singleton
class TokenAuthenticator(
val authService: Lazy<AuthService>,
private val sharedPreferences: SharedPreferences
) : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
return authRequestWithNewToken(response.request)
}
private fun authRequestWithNewToken(
request: Request
): Request? {
getFreshAccessToken()?.let { freshToken ->
return getNewRequest(request, freshToken)
}
return null
}
private fun getFreshAccessToken(): String? {
val refreshResponse = authService.get().refreshTokenTemp().execute()
if (refreshResponse.isSuccessful) {
val updatedAccessToken =
refreshResponse.body()?.data?.accessToken ?: return null
val updatedRefreshToken =
refreshResponse.body()?.data?.refreshToken ?: return null
updateToken(updatedAccessToken, updatedRefreshToken)
Timber.tag("TOKEN")
.d("Auth Updated token\n AccessToken: $updatedAccessToken \n RefreshToken: $updatedRefreshToken")
return updatedAccessToken
} else {
return null
}
}
private fun getNewRequest(request: Request, accessToken: String): Request {
return request.newBuilder()
.header(
AppConstants.PARAMS.AUTH,
AppConstants.PARAMS.BEARER + accessToken
)
.header("Accept", "application/json")
.header("User-Agent", AppConstants.PARAMS.USER_AGENT)
.build()
}
private fun updateToken(accessToken: String, refreshToken: String) {
with(sharedPreferences.edit()) {
putString(AppConstants.SHAREDPREFERENCE.ACCESS_TOKEN, accessToken)
putString(AppConstants.SHAREDPREFERENCE.REFRESH_TOKEN, refreshToken).apply()
}
AppVariables.SessionData.accessToken = accessToken
AppVariables.SessionData.refreshToken = refreshToken
Timber.tag("TOKEN").d("Token Refreshed")
}
}
Even after using this, since there are parallel API calls happening, my app was still timing out and the user was taken to the Login screen.
I couldn't figure out what was wrong even after spending a lot of time.
I saw people trying @synchronised
and I tried to implement that with no success.
Finally I saw this where he added a dispatcher like this:
@Singleton
@Provides
fun provideOkhttpClient(tokenAuthenticator: TokenAuthenticator): OkHttpClient {
//***********like this**********************//
val dispatcher = Dispatcher()
dispatcher.maxRequests = 1
//******************************************//
return OkHttpClient.Builder()
.dispatcher(dispatcher)
.connectTimeout(180, TimeUnit.SECONDS)
.readTimeout(180, TimeUnit.SECONDS)
.writeTimeout(180, TimeUnit.SECONDS)
.authenticator(tokenAuthenticator)
.addInterceptor(TokenInterceptor())
.build()
}
which seems to have fixed my issue. However, I don't know the implications of this. Is this recomended to use in production?
I can see the following in the documentation:
The maximum number of requests to execute concurrently. Above this requests queue in memory, waiting for the running calls to complete. If more than maxRequests requests are in flight when this is invoked, those requests will remain in flight.
Am I missing something that might bite me in the ass later??