Please I need some help. I am running an Interceptor to detect if the access token is expired or not. So If expired I am trying to refresh Token otherwise I send the request.
My issue that it is running the refresh token request with the token on the header and the other requests with the refresh token on the header. I did some research, they say that it is a problem of concurrency. as I am new to that I think I used the wrong way.
Please anyone can help me to fix this issue. Here is my code.
class ApolloInterceptor @Inject constructor(
private val loginViewModel: Lazy<LoginViewModel>, private val context: Context, private var storage: Storage,
) : HttpInterceptor {
private val mutex = Mutex()
private var isRefreshTokenInProgress = false
override suspend fun intercept(request: HttpRequest, chain: HttpInterceptorChain): HttpResponse {
// retrieve current token and refresh token
val token = mutex.withLock {
storage.getString(SharedPreferencesStorage.TOKEN)
}
val refreshToken: String = mutex.withLock {
storage.getString(SharedPreferencesStorage.REFRESH_TOKEN)
}
// convert token expiration date and now date to UTC
val tokenExpirationDate = storage.getString(SharedPreferencesStorage.TOKEN_EXPIRATION_DATE)
val tokenExpirationDateTime = LocalDateTime.parse(tokenExpirationDate)
val nowDateTime = LocalDateTime.now()
val tokenExpirationUtc = ZonedDateTime.of(tokenExpirationDateTime, ZoneId.systemDefault()).withZoneSameInstant(ZoneId.of("UTC"))
val currentDateTimeUtc = ZonedDateTime.of(nowDateTime, ZoneId.systemDefault()).withZoneSameInstant(ZoneId.of("UTC"))
val isTokenExpired = currentDateTimeUtc.isAfter(tokenExpirationUtc)
if (isTokenExpired && !isRefreshTokenInProgress) {
// refresh token
mutex.withLock {
isRefreshTokenInProgress = true
loginViewModel.get().refreshToken().join()
return chain.proceed(request.newBuilder().addHeader("Authorization", "Bearer $refreshToken").build())
}
} else {
// otherwise send request
mutex.withLock {
val requestBuilder = request.newBuilder()
val response = chain.proceed(requestBuilder.addHeader("Authorization", "Bearer $token").build())
val versionName = BuildConfig.VERSION_NAME
val versionCode = BuildConfig.VERSION_CODE
requestBuilder.addHeader("version", versionName)
requestBuilder.addHeader("build", versionCode.toString())
requestBuilder.addHeader("User-Agent", getDefaultUserAgent())
requestBuilder.addHeader("language", getDeviceLanguage())
requestBuilder.addHeader("theme", if (isDarkModeActive(context)) "dark" else "light")
Log.e("TAG", "intercept: dkhal request")
isRefreshTokenInProgress = false
return response
}
}
}
}
I tried to use Mutex.withLock and I did some research for how to implement an interceptor for Apollo. I am expecting to refresh the token and then the previous request will be launched automatically.
1 Answer
Finally I will answer my question. According to the Apollo documentation you need to do the following. It is working like a charm in my code. I wish it can help someone else.
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
}
}
}