Hello, How do I invalidate access tokens without w...
# ktor
a
Hello, How do I invalidate access tokens without waiting for 401? apart from refresh and access tokens, I get accessTokenExpiresInSeconds value and I want to invalidate the access token before making a request. Is this what
sendWithoutRequest
does? The documentation says
Sends credentials without waiting for HttpStatusCode.Unauthorized.
, but I can't figure out how to set it up
a
You can clear the tokens storage by calling the
BearerAuthProvider.clearToken
method. Here, you can find the code example.
a
@Aleksei Tirman [JB] but it also invalidates the refresh token right? I want to keep the refresh token, make the refreshToken request, and then send the actual request using the new access token
a
That's right.
You can send the refresh token request manually and replace the current access token using the MutableStateFlow. Here is an example:
Copy code
fun main(): Unit = runBlocking {
    val initialTokens = BearerTokens("initial", "")
    val tokensFlow = MutableStateFlow(initialTokens)

    val client = HttpClient(CIO) {
        install(Auth) {
            bearer {
                sendWithoutRequest { true }
                loadTokens {
                    tokensFlow.value
                }

                refreshTokens {
                    BearerTokens("new", "")
                }
            }
        }
    }

    val tokens = client.refresh()
    tokensFlow.compareAndSet(initialTokens, tokens)

    val r = client.get("<https://httpbin.org/bearer>")
    println(r.bodyAsText())
}

suspend fun HttpClient.refresh(): BearerTokens {
    return BearerTokens("refreshed", "")
}
a
@Aleksei Tirman [JB] sorry for a late reply, i tried what you suggested, and now i'm stuck with the following problem:
Copy code
interface AuthProvider {
    ...
    suspend fun refreshToken(response: HttpResponse): Boolean
}
takes an
HttpResponse
as a parameter
so I ended up writing
Copy code
fun provideDummyResponse(client: HttpClient): HttpResponse {
    return object : HttpResponse() {
        override val call: HttpClientCall
            get() = HttpClientCall(client)
        @InternalAPI override val content: ByteReadChannel
            get() = error("Unreachable code")
        override val coroutineContext: CoroutineContext
            get() = error("Unreachable code")
        override val headers: Headers
            get() = error("Unreachable code")
        override val requestTime: GMTDate
            get() = error("Unreachable code")
        override val responseTime: GMTDate
            get() = error("Unreachable code")
        override val status: HttpStatusCode
            get() = error("Unreachable code")
        override val version: HttpProtocolVersion
            get() = error("Unreachable code")
    }
}
do you know if there is a more elegant way to do this?
a
Where the
HttpResponse
object come from and how it's used in the
refreshToken
method body?
a
it is passed to me to refresh tokens
Copy code
public override suspend fun refreshToken(response: HttpResponse): Boolean {
    val newToken = tokensHolder.setToken {
        refreshTokens(RefreshTokensParams(response.call.client, response, tokensHolder.loadToken()))
    }
    return newToken != null
}
a
So do you need the original response to obtain the new access token?
a
no, i want to refresh the token without a response
a
Then I don't understand the problem
a
in my case I want to open the app, it wants to make a request but sees that the access token is expired, and instead of making that request, makes the refresh tokens request
and the problem I'm having is that refresh tokens request has a
HttpResponse
as a parameter when i have just opened the app and haven't done a single request