ktorio / ktor

Framework for quickly creating connected applications in Kotlin with minimal effort
https://ktor.io
Apache License 2.0
12.79k stars 1.04k forks source link

Add OAuth2/Space client authentication #1595

Open vladimirdolzhenko opened 4 years ago

vladimirdolzhenko commented 4 years ago

Subsystem Client

Is your feature request related to a problem? Please describe. There is no oauth2 client authentication, and especially JetBrains Space auth

Describe the solution you'd like I'd like to have smth like

val spaceClient = client.config {
    Auth {
       Space(clientId, clientSecret) 
    }
}

actually to authenticate in Space I have to do smth like

private data class AccessToken(val token_type: String, val expires_in: Int, val access_token: String)

private suspend fun issueAccessToken(endpoint: String, clientId: String, clientSecret: String): AccessToken {
        return client.submitForm(
            url = "${endpoint}/oauth/token",
            formParameters = Parameters.build {
                append("grant_type", "client_credentials")
                append("scope", "**")
            }
        ) {
            header(
                HttpHeaders.Authorization,
                HttpAuthHeader.Single(
                    AuthScheme.Basic,
                    String(Base64.getEncoder().encode("$clientId:$clientSecret".toByteArray()))
                ).render()
            )
        }
    }

and later on for each query use

private fun HttpRequestBuilder.authHeader() {
        header(
            HttpHeaders.Authorization,
            HttpAuthHeader.Single(issueAccessToken.token_type, issueAccessToken.access_token).render()
        )
    }

Motivation to include to ktor OAuth2 is quite common auth way

hardysim commented 4 years ago

I tried to write my own OAuthAuthProvider based on the existing providers in io.ktor.client.features.auth.providers which seems to work but with the restriction that I can only retry a request once.

But I might need to retry the request multiple times when using a refresh token:

  1. try request
  2. request fails with 401
  3. the provider is asked to add an authentication-header
    • it does by adding the current access token
  4. request is retried with the added header by the auth-feature
  5. request fails again because the token is invalid
  6. now the provider should
    • invalidate the current access token
    • get a new access token (via refresh token or username/password)
  7. retry the request one more time with the new access token
  8. request is ok or fails again (start over or stop)

So my question is: How can I retry the request multiple times in an io.ktor.client.features.auth.AuthProvider?

PS: When my provider is working, I'm happy to contribute it here.

e5l commented 4 years ago

Hi, @hardysim. You can try using ‘HttpSend’ feature. You can add interceptor there and retry request multiple times(See HttpRedirect feature as the example)

hardysim commented 4 years ago

I've managed to alter the Auth feature to behave a bit like OkHttp's Authenticator.

This means, AuthProvider.addRequestHeaders() can return a nullable request (instead of just altering it) and Auth will execute() the request if one is returned (and stops when null is returned). This way, no circuitBreaker is needed and the provider can retry the request as often as it likes to.

I'm going to post a PR with my changes. Maybe the team will accept my solution and you'll get my OAuthAuthProvider with it.

oleg-larshin commented 4 years ago

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.

hardysim commented 3 years ago

To crosslink the progress:

fullkomnun commented 3 years ago

Is there any update regarding this issue?

Stexxe commented 1 year ago

Please follow https://youtrack.jetbrains.com/issue/KTOR-5232/