https://kotlinlang.org logo
#ktor
Title
# ktor
s

Stefan Oltmann

10/26/2021, 10:13 AM
Does someone use Ktor with the Dropbox API? There seems to be a bug that Dropbox can't be used while Google is fine. https://youtrack.jetbrains.com/issue/KTOR-3332
a

Aleksei Tirman [JB]

10/26/2021, 1:18 PM
The simplest code works as expected
Copy code
import io.ktor.client.*
import io.ktor.client.engine.apache.*
import io.ktor.client.features.json.*
import io.ktor.client.features.json.serializer.*
import io.ktor.client.request.*
import io.ktor.http.*
import kotlinx.serialization.Serializable

suspend fun main(args: Array<String>) {
    val client = HttpClient(Apache) {
        install(JsonFeature) {
            serializer = KotlinxSerializer(kotlinx.serialization.json.Json {
                ignoreUnknownKeys = true
            })
        }
    }

    val accessToken = "sl.A7G7Mw5SveSZQ7j9OX1HFRv3d-VkP3IPoqfhEzfJz-yNSKOdDiQa-eVQ53TVCHr_KXKh5Iwc6OFHQttOJ_y0pxX8DctT9E0tJ_eLetLQHzpxUoFaNXESjDwfvt7mvCIOtzoC2RU"
    val result = <http://client.post|client.post><AccountResponse>("<https://api.dropboxapi.com/2/users/get_current_account>") {
        header(HttpHeaders.Authorization, "Bearer $accessToken")
    }

    println(result.name.display_name)
}

@Serializable
data class AccountResponse(val account_id: String, val name: AccountName)
@Serializable
data class AccountName(val display_name: String)
Could you please share the code for Bearer authentication?
s

Stefan Oltmann

10/26/2021, 1:21 PM
That looks like this:
Copy code
fun createClient(
    oAuthConfig: OAuthConfig,
    loadTokenPair: () -> OAuthTokenPair,
    saveNewTokenPair: (OAuthTokenPair) -> Unit
): HttpClient {

    return HttpClient(CIO) {
        install(Auth) {

            expectSuccess = false

            install(JsonFeature) {
                serializer = KotlinxSerializer()
            }

            bearer {

                loadTokens {

                    val tokenPair = loadTokenPair()

                    BearerTokens(
                        accessToken = tokenPair.accessToken,
                        refreshToken = tokenPair.refreshToken!!
                    )
                }

                refreshTokens {

                    val tokenPair = loadTokenPair()

                    val newTokenPair = refreshTokenPair(
                        oAuthConfig = oAuthConfig,
                        refreshToken = tokenPair.refreshToken!!
                    )

                    val toSavePair = OAuthTokenPair(
                        accessToken = newTokenPair.accessToken,
                        refreshToken = tokenPair.refreshToken
                    )

                    saveNewTokenPair(toSavePair)

                    BearerTokens(
                        accessToken = toSavePair.accessToken,
                        refreshToken = toSavePair.refreshToken!!
                    )
                }
            }
        }
    }
}
I don't understand why this code in contrast works:
Copy code
private suspend fun refreshTokenPair(
    tokenUrl: String,
    refreshToken: String,
    clientId: String,
    clientSecret: String?,
    redirectUri: String?
): OAuthTokenPair {

    val tokenClient = HttpClient(CIO) {
        install(JsonFeature) {
            serializer = KotlinxSerializer(
                Json {
                    ignoreUnknownKeys = true
                }
            )
        }
    }

    return tokenClient.submitForm(
        url = tokenUrl,
        formParameters = Parameters.build {
            append("grant_type", "refresh_token")
            append("refresh_token", refreshToken)
            append("client_id", clientId)
            // redirectUri?.let { redirectUri -> append("redirect_uri", redirectUri) }
            clientSecret?.let { clientSecret -> append("client_secret", clientSecret) }
        }
    )
}
If that's important: I use Ktor 1.6.4 with KotlinX Serialization 1.3.0 and Kotlin 1.5.31. All in commonMain of my shared module.
Your sample code also works. I had to change it to "CIO" because of multiplatform. And of course I used my own access token.
Copy code
suspend fun getDisplayName() {
    val client = HttpClient(CIO) {
        install(JsonFeature) {
            serializer = KotlinxSerializer(kotlinx.serialization.json.Json {
                ignoreUnknownKeys = true
            })
        }
    }

    val accessToken = "mytoken"
    val result = <http://client.post|client.post><AccountResponse>("<https://api.dropboxapi.com/2/users/get_current_account>") {
        header(HttpHeaders.Authorization, "Bearer $accessToken")
    }

    println(result.name.display_name)
}

@Serializable
data class AccountResponse(val account_id: String, val name: AccountName)
@Serializable
data class AccountName(val display_name: String)
OK! I got it.
Copy code
install(JsonFeature) {
    serializer = KotlinxSerializer()
}
must be on the same level as
install(Auth)
, not inside.
Inside it seems to be doing nothing. I wonder why this does not give any error. My fault. Thanks for your help.
👍 1
a

Aleksei Tirman [JB]

10/26/2021, 1:41 PM
May I close the KTOR-3332?
s

Stefan Oltmann

10/26/2021, 1:41 PM
Yes, thank you.
2 Views