5

I am using retrofit for networking in my project. The problem is I have to call 2 requests from my first activity. It works fine but when the access token expires it has to refresh token. I have implemented a call using okhttp Authenticator. But it is calling multiple times and this error is showing too too many followup request 21

EDIT I updated TokenAuthenticator class and added synchronized(). but it is returning from if (originalRequest.header("Authorization") != null) return null . I am following this answer https://stackoverflow.com/a/52513122/10243953

If i am removing if (originalRequest.header("Authorization") != null) return null this line then its working but in log report i see its calling for refresh token multiple times. How can i avoid this multiple time calls?

This is my Authenticator class

class TokenAuthenticator : Authenticator {

private val refreshTokenGrandType = "refresh_token"
private var oldToken: String? = null
private var newToken: String? = null

override fun authenticate(route: Route?, response: Response?): Request? {
    oldToken = SharedPreferenceManager(MainApplication.applicationContext()).getToken()
    if (response == null) return null
    val originalRequest = response.request()
    if (originalRequest.header("Authorization") != null) return null
    if(!isTokenSaved()){
        synchronized(this) {
            RetrofitClient.client.create(Auth::class.java).refresh_token(
                SharedPreferenceManager(MainApplication.applicationContext()).getRefreshToken()!!,
                refreshTokenGrandType
            ).enqueue(object : Callback<Token> {
                override fun onFailure(call: Call<Token>, t: Throwable) {
                    Toast.makeText(
                        MainApplication.applicationContext(),
                        t.message,
                        Toast.LENGTH_SHORT
                    ).show()
                    Log.d("TokenAuth", t.message!!)
                }

                override fun onResponse(
                    call: Call<Token>,
                    response: retrofit2.Response<Token>
                ) {
                    if (response.isSuccessful) {
                        val body = response.body()
                        newToken = body!!.access_token
                        val refresh_token = body.refresh_token

                        SharedPreferenceManager(MainApplication.applicationContext()).accessToken(
                            newToken!!,
                            refresh_token
                        )

                    } else {
                        val error = response.errorBody()
                        Log.d("TokenAuthRes", error!!.string())
                    }
                }
            })
        }
    }
    return originalRequest
        .newBuilder()
        .header(
            "Authorization",
            "Bearer ${SharedPreferenceManager(MainApplication.applicationContext()).getToken()}"
        )
        .build()

}

fun isTokenSaved() : Boolean{
    if (newToken == null) return false
    if (oldToken.equals(newToken)) return false
    else return true
}
}

Retrofit client

object RetrofitClient {
private lateinit var interceptor : Interceptor
private lateinit var okHttpClient: OkHttpClient
private var retrofit : Retrofit? = null

val client : Retrofit
    get(){
        val context : Context = MainApplication.applicationContext()
        interceptor = Interceptor { chain ->
            val url = chain.request()
                .url()
                .newBuilder()
                .build()

            val request = chain.request()
                .newBuilder()
                .addHeader("Authorization","Bearer ${SharedPreferenceManager(context).getToken()}")
                .url(url)
                .build()

            return@Interceptor chain.proceed(request)
        }

        okHttpClient = OkHttpClient.Builder()
            .addInterceptor(interceptor)
            .addInterceptor(NoInternetInterception(context))
            .authenticator(TokenAuthenticator())
            .connectTimeout(1, TimeUnit.MINUTES)
            .build()

        if (retrofit == null){
            retrofit = Retrofit.Builder()
                .client(okHttpClient)
                .baseUrl(const.URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
        }
        return retrofit!!
    }
 }
Ajay Gohel
  • 243
  • 7
  • 17
  • Did you were able to fix this? – Rohit Maurya Oct 30 '20 at 14:37
  • I have no answer for your question but i had similar problems using retrofit. After searching a bit i found that retrofit removes auth information from the header of the original request. It is useless then to check originalRequest.header("authorization") because it will always be null. You could read this: https://github.com/square/retrofit/issues/977 As a workaround i was looking at the response whether it was unauthorized and if so i tried to obtain a token since obviously the request went to an protected endpoint. – arnonuem Feb 28 '22 at 14:31
  • You should inside synchronized if the token has been already refreshed before refreshing it – Iñigo Bereciartua Aug 03 '22 at 08:22

0 Answers0