Apply ktor-client-auth library to auto refresh token

This commit is contained in:
2026-05-15 22:40:09 +07:00
parent e7c381a953
commit 28901df627
10 changed files with 116 additions and 80 deletions
@@ -10,6 +10,9 @@ import io.ktor.client.engine.darwin.Darwin
import io.ktor.client.plugins.DefaultRequest
import io.ktor.client.plugins.HttpSend
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.auth.Auth
import io.ktor.client.plugins.auth.providers.BearerTokens
import io.ktor.client.plugins.auth.providers.bearer
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
@@ -18,22 +21,40 @@ import io.ktor.client.plugins.plugin
import io.ktor.client.request.header
import io.ktor.http.HttpStatusCode
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.Json
actual fun createPlatformHttpClient(
tokenManager: TokenManager,
authApi: AuthApi?
): HttpClient {
val client = HttpClient(Darwin) {
return HttpClient(Darwin) {
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
isLenient = true
})
}
install(DefaultRequest) {
tokenManager.getAccessToken()?.let {
header("Authorization", "Bearer $it")
install(Auth) {
bearer {
loadTokens {
val access = tokenManager.getAccessToken()
val refresh = tokenManager.getRefreshToken()
if (access != null && refresh != null) {
BearerTokens(access, refresh)
} else null
}
refreshTokens {
Napier.d(">>>>> [NetworkEngine.ios.kt] Access token expired, auto request refresh token...")
val newAccess = authApi?.refreshToken()
if (newAccess != null) {
BearerTokens(newAccess, tokenManager.getRefreshToken()!!)
} else {
tokenManager.clearTokens()
tokenManager.triggerSessionExpired()
throw ForceLogoutException()
}
}
}
}
install(HttpTimeout) { requestTimeoutMillis = 30_000 }
@@ -46,30 +67,4 @@ actual fun createPlatformHttpClient(
level = LogLevel.ALL // set LogLevel.NONE for release
}
}
if (authApi != null) {
client.plugin(HttpSend).intercept { request ->
// Skip interceptor for refresh token request to avoid recursion
if (request.url.pathSegments.contains(ReferKeys.REFRESH_TOKEN)) {
return@intercept execute(request)
}
val originalCall = execute(request)
if (originalCall.response.status == HttpStatusCode.Unauthorized) {
Napier.d(">>>>> [NetworkEngine.ios.kt] Access token expired, auto request refresh token...")
val newToken = authApi.refreshToken()
if (newToken != null) {
request.headers["Authorization"] = "Bearer $newToken"
execute(request)
} else {
tokenManager.clearTokens()
throw ForceLogoutException()
}
} else {
originalCall
}
}
}
return client
}
}