Md Hanif
Md Hanif

Reputation: 1107

Refresh auth token in Ktor for iOS Http client

I have a KMM project in which I have been using Ktor for the API calls. I have a requirement in which I need to update my access token with the help of refresh tokens if they are expired. Basically I just need to add an authentication module in my Ktor client. No I have gone through all Ktor documentation and added Auth module in my KMM.

Now when I add auth module in my http client it gets added successfully and whenever I receive UnAuthorized user error from any API it calls my refresh token API. The issue is even though it calls my refresh token API but on success of refresh token it does not call the other API from which I have received UnAuthorized user error.

It works as expected in Android but the only issue is in iOS client.

Expected (Works fine in Android Http client) :-

Issue I am facing :-

HttpClient for iOS :-

actual class HttpBaseClient {

    actual val tokenClient = HttpClient {
        defaultRequest {
            host = ApiEndPoints.Base.url
            url {
                protocol = URLProtocol.HTTPS
            }
            contentType(ContentType.Application.Json)
            header(CONNECTION, CLOSE)
        }
        install(JsonFeature) {
            val json = kotlinx.serialization.json.Json {
                ignoreUnknownKeys = true
                coerceInputValues = true
            }
            serializer = KotlinxSerializer(json)
        }
    }

    actual val httpClient: HttpClient = HttpClient {
        defaultRequest {
            host = ApiEndPoints.Base.url
            url {
                protocol = URLProtocol.HTTPS
            }
            contentType(ContentType.Application.Json)
            header(CONNECTION, CLOSE)
        }
        // Validate Response
        expectSuccess = false
        // Install Auth
        install(Auth) {
            lateinit var refreshTokenInfo : LoginResponse
            bearer {
                refreshTokens { unauthorizedResponse: HttpResponse ->
                    NSLog("Unauthorized response received")
                    BaseAPIClass().refreshAuthToken().fold(
                        failed = {
                            // On Failed
                            NSLog("Token Failed"). // No Callback received here
                        },
                        succeeded = { response ->
                            refreshTokenInfo = response
                            NSLog("Token Updated") // No Callback received here even when API is success
                        }
                    )
                    BearerTokens(
                        accessToken = refreshTokenInfo.accessToken ?: "",
                        refreshToken = refreshTokenInfo.refreshToken ?: ""
                    )
                }
            }
        }
        // JSON Deserializer
        install(JsonFeature) {
            val json = kotlinx.serialization.json.Json {
                ignoreUnknownKeys = true
                coerceInputValues = true
            }
            serializer = KotlinxSerializer(json)
        }

Android Client (Pretty much same):-

actual class HttpBaseClient {

    actual val tokenClient = HttpClient {
        defaultRequest {
            host = ApiEndPoints.Base.url
            url {
                protocol = URLProtocol.HTTPS
            }
            contentType(ContentType.Application.Json)
            header(CONNECTION, CLOSE)
        }
        install(JsonFeature) {
            val json = kotlinx.serialization.json.Json {
                ignoreUnknownKeys = true
                coerceInputValues = true
            }
            serializer = KotlinxSerializer(json)
        }
        install(Logging) {
            logger = Logger.DEFAULT
            level = LogLevel.ALL
        }
    }

    actual val httpClient: HttpClient = HttpClient {
        defaultRequest {
            host = ApiEndPoints.Base.url
            url {
                protocol = URLProtocol.HTTPS
            }
            contentType(ContentType.Application.Json)
            header(CONNECTION, CLOSE)
        }
        // Validate Response
        expectSuccess = false
        //Authentication
        install(Auth) {
            lateinit var refreshTokenInfo : LoginResponse
            bearer {
                refreshTokens { unauthorizedResponse: HttpResponse ->
                    BaseAPIClass().refreshAuthToken().fold(
                        failed = {
                            // On Failed
                        },
                        succeeded = { response ->
                            refreshTokenInfo = response
                        }
                    )
                    BearerTokens(
                        accessToken = refreshTokenInfo.accessToken ?: "",
                        refreshToken = refreshTokenInfo.refreshToken ?: ""
                    )
                }
            }
        }

Ktor version :- 1.6.2 (tried 1.6.4 as well after reading this issue but didn't work)

Upvotes: 3

Views: 3192

Answers (1)

Md Hanif
Md Hanif

Reputation: 1107

I got this working only the error is at this code :-

succeeded = { response ->
                        refreshTokenInfo = response
                        NSLog("Token Updated") // No Callback received here even when API is success
                    }

I am not sure why but assigning the response to my lateinit var refreshTokenInfo is causing the main problem here. I removed that and updated my code to

refreshTokens { unauthorizedResponse: HttpResponse ->
                BaseAPIClass().refreshAuthToken().fold(
                    failed = {
                        // On Failed
                        return@refreshTokens BearerTokens(
                            accessToken = "",
                            refreshToken = ""
                        )
                    },
                    succeeded = { response ->
                      return@refreshTokens BearerTokens(
                            accessToken = response.accessToken ?: "",
                            refreshToken = response.refreshToken ?: ""
                        )
                    }
                )
            }

and this works.

I have also raised the issue here you can go through the details.

Upvotes: 0

Related Questions