GraphQL Refresh Token in Kotlin

GraphQL Refresh Token in Kotlin


0

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
1


0

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
  }
 }
}



Leave a Reply

Your email address will not be published. Required fields are marked *