I’m trying to add kmp (ktor) to existing ios app w...
# ktor
u
I’m trying to add kmp (ktor) to existing ios app which uses Alamofire. I have trouble with token management, i.e. how to keep the 2 in sync. Is there a way to tell ktor client that tokens were updated, other than getting s 401?
a
You can use the
BearerAuthProvider.clearToken
method to remove the currently stored tokens:
Copy code
client.authProvider<BearerAuthProvider>()?.clearToken()
u
but how will i set one? does that rely on calling loadTokens again? Is there a way to have it not store it in memory and always call loadTokens for every request?
a
Unfortunately, it's not possible. See KTOR-4759 for more information.
u
what should be the outcome of the ticket? a new non caching auth provider? or changing the behavior of current one?
a
Most likely, it's going to be a new non-caching Bearer auth provider.
u
Means in the meantime I can implement it myself? Is it feasible?
Copy code
object StatelessAuth {
    class Config {
        // Always fetch the token fresh; you control where it comes from (disk, keystore, IPC, etc.)
        lateinit var tokenProvider: suspend () -> String
        // Optional: called after a 401 to refresh credentials
        var refresh: (suspend () -> Unit)? = null
        var headerName: String = HttpHeaders.Authorization
        var scheme: String = "Bearer"
        var retryOnUnauthorized: Boolean = true
        var maxRetries: Int = 1
    }

    val Plugin = createClientPlugin("StatelessAuth", ::Config) {
        val tokenProvider = pluginConfig.tokenProvider
        val refresh = pluginConfig.refresh
        val headerName = pluginConfig.headerName
        val scheme = pluginConfig.scheme
        val retryOnUnauthorized = pluginConfig.retryOnUnauthorized
        val maxRetries = pluginConfig.maxRetries

        suspend fun applyAuth(builder: HttpRequestBuilder) {
            val token = tokenProvider()
            builder.headers.remove(headerName)
            builder.headers.append(headerName, if (scheme.isEmpty()) token else "$scheme $token")
        }

        on(Send) { original ->
            // 1) send with fresh token (no caching in the plugin)
            applyAuth(original)
            var call = proceed(original)

            // 2) optional retry on 401 without keeping any token in memory
            var attempts = 0
            while (retryOnUnauthorized && call.response.status == HttpStatusCode.Unauthorized && attempts < maxRetries) {
                attempts += 1
                refresh?.invoke()

                val retry = HttpRequestBuilder().takeFrom(call.request) // rebuild same request
                applyAuth(retry)
                call = proceed(retry)
            }
            call
        }
    }
}
How does something like this look to you?
a
I think it will be easier to add a custom auth provider by implementing the
AuthProvider
interface instead of writing another client plugin.