Search code examples
androidretrofit2okhttpkotlin-coroutinesauthenticator

OKHttp Authenticator not working with Retrofit suspend fun


I recently updated Retrofit to 2.7.0 and OKHttp to 3.14.4 to take advantage of suspend fun on Retrofit interfaces.

Besides that, I'm also trying to implement Authenticator for the refresh token logic.

This is the retrofit interface

interface OfficeApi {
    @Authenticated
    @POST
    suspend fun getCharacter(): Response<CharacterResponse>
}

This is my Authenticator

class CharacterAuthenticator : Authenticator {

    override fun authenticate(
        route: Route?,
        response: Response
    ): Request? {
        if (responseCount(response) >= 2) return null

        return response.request()
                        .newBuilder()
                        .removeHeader("Authorization")
                        .addHeader("Authorization", "Bearer $newToken")
                        .build()

        return null
    }

    private fun responseCount(response: Response?): Int {
        var result = 1
        while (response?.priorResponse() != null) result++
        return result
    }

}

This is the retrofit fun call

    override suspend fun getCharacter() = safeApiCall(moshiConverter) {
        myApi.getCharacter()
    }

This is the safeApiCall:

suspend fun <T> safeApiCall(
    moshiConverter: MoshiConverter,
    apiCall: suspend () -> Response<T>
): Result<T?, ResultError.NetworkError> {
    return try {
        val response = apiCall()
        if (response.isSuccessful) Result.Success(response.body())
        else {
            val errorBody = response.errorBody()
            val errorBodyResponse = if (errorBody != null) {
                moshiConverter.fromJsonObject(errorBody.string(), ErrorBodyResponse::class.java)
            } else null

            Result.Error(
                ResultError.NetworkError(
                    httpCode = response.code(),
                    httpMessage = response.message(),
                    serverCode = errorBodyResponse?.code,
                    serverMessage = errorBodyResponse?.message
                )
            )
        }
    } catch (exception: Exception) {
        Result.Error(ResultError.NetworkError(-1, exception.message))
    }
}

The Authenticator is working properly, trying to refresh the token twice and then giving up. The problem is: when it gives up (return null), the execution of retrofit (safeApiCall function) does not continue. I don't have any feedback if the call was successful or not.

Is there any problem using Authenticator and Coroutines suspend fun?


Solution

  • Isn't this an infinite loop?

    while (response?.priorResponse() != null)
    

    Shouldn't it be

    var curResponse: Response? = response
    while (curResponse?.priorResponse() != null) {
        result++
        curResponse = curResponse.priorResponse()
    }