kotlin 使用Ktor进行Spotify PCKE授权

9rygscc1  于 2022-11-30  发布在  Kotlin
关注(0)|答案(1)|浏览(167)

我正在使用JetBrains ComposeKtor构建我的第一个桌面应用程序。我想使用Spotify Auth PCKE extension连接到Spotify API。流程应该是
1.向accounts. spotify. com/authorize发送获取身份验证请求
1.如果用户未授权-〉启动网页供用户登录

  1. Spotify向初始请求中提供的URL发送回调。
    1.从Spotify接收代币
    我在启动用户登录网页并在我提供的URL上获得响应时遇到了问题。我使用桌面的默认浏览器启动网页,并打开了一个应该监听"http://localhost:8888/callback)"的websocket。我注意到,当我与启动的网页交互时,URL显示Spotify返回的响应("http://localhost:8888/callback?error=access_denied&state=initial"),但我的websocket代码从未被调用。这是我启动浏览器或websocket的方式的问题...还是我在总体上做错了?
class SpotifyClient {
    private val client: HttpClient = HttpClient(CIO) {
        followRedirects = true
        install(WebSockets) {
            contentConverter = KotlinxWebsocketSerializationConverter(Json)
        }
        handleSpotifyAccountAuthResponse()
    }


    private val clientId = "<Removed for Post>"

    suspend fun authorizeSpotifyClient(): HttpResponse {

        val withContext = withContext(Dispatchers.IO) {

            val response: HttpResponse =
                client.get(NetworkConstants.BASE_URL_SPOTIFY_ACCOUNTS + NetworkConstants.PATH_SPOTIFY_AUTH) {
                    header("Location", NetworkConstants.BASE_URL_SPOTIFY_AUTH_REDIRECT_URI)
                    parameter("client_id", clientId)
                    parameter("response_type", "code")
                    parameter("redirect_uri", NetworkConstants.BASE_URL_SPOTIFY_AUTH_REDIRECT_URI)
                    parameter("state", ClientStates.INITIAL.value)
                    parameter("show_dialog", false)
                    parameter("code_challenge_method", PCKE_CODE_CHALLENGE_METHOD)
                    parameter("code_challenge", generatePCKECodeChallenge())
                    parameter(
                        "scope",
                        NetworkUtils.getSpotifyScopes(
                            ImmutableList.of(
                                SpotifyScopes.USER_READ_PLAYBACK_STATE,
                                SpotifyScopes.USER_READ_CURRENTLY_PLAYING
                            )
                        )
                    )

                }
            println("Auth Spotify API Response $response")
            println("Auth Spotify API Response Code ${response.status}")
            client.close()
            return@withContext response
        }
        accountAuthRedirectWebsocket()
        return withContext
    }

    private fun HttpClientConfig<CIOEngineConfig>.handleSpotifyAccountAuthResponse() {
        HttpResponseValidator {

            validateResponse { response ->
                handleValidAccountResponse(response)
            }

            handleResponseExceptionWithRequest { exception, request ->

            }
        }
    }

    private fun handleValidAccountResponse(response: HttpResponse) {
        if (response.status.value == 200) { // success
            val responseUrl = response.call.request.url
            if (responseUrl.toString().contains("continue")) {
                println("Needs a redirect, ${responseUrl.toString()}")
                openWebpage(responseUrl.toURI())
            }

        }
    }


    private fun openWebpage(uri: URI?): Boolean {
        val desktop = if (Desktop.isDesktopSupported()) Desktop.getDesktop() else null
        if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
            try {
                desktop.browse(uri)

                return true
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        return false
    }

    private suspend fun accountAuthRedirectWebsocket(){
        client.webSocket(method = HttpMethod.Get, host = "localhost", port = 8888, path = "/customer/1") {
            println("Got a response in the socket")
        }
    }
des4xlb0

des4xlb01#

为了接收回调,我需要实现一个如下所示的本地服务器。在这种情况下,使用WebSocket是错误的方法,并且没有提供我所期望的功能。

object SpotifyServer {

    suspend fun initServer() {
        embeddedServer(CIO,
            host = "127.0.0.1",
            port = 8080,
            configure = {}) {
            routing {
                get("/callback") {
                        call.respondText("Got a callback response")

                }
            }
        }.start(wait = true)
    }
}

相关问题