OKHttp Authenticator not working with Retrofit suspend fun
Asked Answered
B

2

6

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?

Backbend answered 11/12, 2019 at 17:3 Comment(1)
Possibly see my answer here, which is a tentative "yes" to your question. I had more success switching away from coroutines to a Call<T> request. #62950938Catechetical
H
3

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()
}
Harrisonharrod answered 11/12, 2019 at 17:48 Comment(0)
R
-4

Remove suspend try following code

fun getCharacter(): Response<CharacterResponse>
Reify answered 11/12, 2019 at 17:6 Comment(1)
The whole idea is to use the new suspend fun support from Retrofit, so removing is not an alternative.Backbend

© 2022 - 2024 — McMap. All rights reserved.