尝试在Ktor中设置OAuth,出现providerLookup问题。

huangapple go评论64阅读模式
英文:

Trying to set up OAuth with Ktor, providerLookup problems

问题

我正在尝试为Kotlin Ktor设置OAuth。我尝试遵循文档,但在第2步配置部分上遇到问题。当我的程序运行时:

call.request.queryParameters["redirectUrl"]!!

我得到一个空指针异常。在调试器模式下运行程序时,我可以看到call/request/queryParameters不为空。因此,应该是"redirectUrl"没有"工作"。

我尝试搜索这行代码应该做什么,但无法在任何地方找到解释。我只能在这个文档中找到它的使用。我假设它应该获取一些重定向URL,但我不知道应该从哪里获取。

我该怎么解决空指针异常?

如果有帮助的话,这是我的类:

package com.example.plugins

import io.ktor.server.auth.*
import io.ktor.client.*
import io.ktor.client.engine.apache.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.sessions.*
import io.ktor.server.response.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.routing.*

fun Application.configureSecurity() {
    val redirects = mutableMapOf<String, String>()
    authentication {
        oauth("auth-oauth-google") {
            urlProvider = { "http://localhost:8086/callback" }
            providerLookup = {
                OAuthServerSettings.OAuth2ServerSettings(
                    name = "google",
                    authorizeUrl = "https://accounts.google.com/o/oauth2/auth",
                    accessTokenUrl = "https://accounts.google.com/o/oauth2/token",
                    requestMethod = HttpMethod.Post,
                    clientId = System.getenv("GOOGLE_CLIENT_ID"),
                    clientSecret = System.getenv("GOOGLE_CLIENT_SECRET"),
                    defaultScopes = listOf("https://www.googleapis.com/auth/userinfo.profile"),
                    extraAuthParameters = listOf("access_type" to "offline"),
                    onStateCreated = { call, state ->
                        redirects[state] = call.request.queryParameters["redirectUrl"]!!
                    }
                )
            }
            client = HttpClient(Apache)
        }
    }
    data class MySession(val count: Int = 0)
    install(Sessions) {
        cookie<MySession>("MY_SESSION") {
            cookie.extensions["SameSite"] = "lax"
            cookie.maxAgeInSeconds = 120
            cookie.path = "/testing"
        }
        cookie<UserSession>("USER_SESSION") {
            cookie.extensions["SameSite"] = "lax"
            cookie.maxAgeInSeconds = 120
            cookie.path = "/testingss"
        }
    }
    routing {
        authenticate("auth-oauth-google") {
            get("login") {
                call.respondRedirect("/callback")
            }
            get("/callback") {
                val principal: OAuthAccessTokenResponse.OAuth2? = call.principal()
                call.sessions.set(UserSession(principal!!.state!!, principal.accessToken))
                val redirect = redirects[principal.state!!]
                call.respondRedirect(redirect!!)
            }
        }
        get("/session/increment") {
            val session = call.sessions.get<MySession>() ?: MySession()
            call.sessions.set(session.copy(count = session.count + 1))
            call.respondText("Counter is ${session.count}. Refresh to increment.")
        }
    }
}
英文:

So Im trying to set up OAuth for Kotlin, Ktor. Im trying to follow the documentation
on but I get stuck on the config part on step 2. When my program runs:

call.request.queryParameters[&quot;redirectUrl&quot;]!!

I get a null point exeption. When running the program in a debugger mode I can see that call/request/queryParameters is not null. So it should be the "redirectUrl" that is not "working".

I have tried to search for what the line is suppose to do but I cant find any explanation of it anywhere. I can only finds its use in this documentation. I assume it is suppose to get some redirecting URLs but I have no clue from where.

What can I do to solve my null point exeption?

Here is my class if it helps

package com.example.plugins
import io.ktor.server.auth.*
import io.ktor.client.*
import io.ktor.client.engine.apache.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.sessions.*
import io.ktor.server.response.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.routing.*
fun Application.configureSecurity() {
val redirects = mutableMapOf&lt;String, String&gt;()
authentication {
oauth(&quot;auth-oauth-google&quot;) {
urlProvider = { &quot;http://localhost:8086/callback&quot; }
providerLookup = {
OAuthServerSettings.OAuth2ServerSettings(
name = &quot;google&quot;,
authorizeUrl = &quot;https://accounts.google.com/o/oauth2/auth&quot;,
accessTokenUrl = &quot;https://accounts.google.com/o/oauth2/token&quot;,
requestMethod = HttpMethod.Post,
clientId = System.getenv(&quot;GOOGLE_CLIENT_ID&quot;),
clientSecret = System.getenv(&quot;GOOGLE_CLIENT_SECRET&quot;),
defaultScopes = listOf(&quot;https://www.googleapis.com/auth/userinfo.profile&quot;),
extraAuthParameters = listOf(&quot;access_type&quot; to &quot;offline&quot;),
onStateCreated = { call, state -&gt;
redirects[state] = call.request.queryParameters[&quot;redirectUrl&quot;]!!
}
)
}
client = HttpClient(Apache)
}
}
data class MySession(val count: Int = 0)
install(Sessions) {
cookie&lt;MySession&gt;(&quot;MY_SESSION&quot;) {
cookie.extensions[&quot;SameSite&quot;] = &quot;lax&quot;
cookie.maxAgeInSeconds = 120
cookie.path = &quot;/testing&quot;
}
cookie&lt;UserSession&gt;(&quot;USER_SESSION&quot;) {
cookie.extensions[&quot;SameSite&quot;] = &quot;lax&quot;
cookie.maxAgeInSeconds = 120
cookie.path = &quot;/testingss&quot;
}
}
routing {
authenticate(&quot;auth-oauth-google&quot;) {
get(&quot;login&quot;) {
call.respondRedirect(&quot;/callback&quot;)
}
get(&quot;/callback&quot;) {
val principal: OAuthAccessTokenResponse.OAuth2? = call.principal()
call.sessions.set(UserSession(principal!!.state!!, principal.accessToken))
val redirect = redirects[principal.state!!]
call.respondRedirect(redirect!!)
}
}
get(&quot;/session/increment&quot;) {
val session = call.sessions.get&lt;MySession&gt;() ?: MySession()
call.sessions.set(session.copy(count = session.count + 1))
call.respondText(&quot;Counter is ${session.count}. Refresh to increment.&quot;)
}
}

}

答案1

得分: 1

如果需要将客户端重定向到其来源URL,请在/login端点添加查询参数redirectUrl(实际名称无关紧要,但必须一致)。以下是工作流程的步骤:

  1. 客户端请求一个未受保护的端点。
  2. 如果会话不存在(客户端未经身份验证),服务器将以查询参数redirectUrl的形式重定向到/login,其中包含请求的URL。
  3. /login端点触发OAuth2身份验证,创建state时,将redirectUrl保存到onStateCreated块内的redirects映射中。
  4. OAuth2提供者将客户端重定向到回调URL,重定向将发生到先前保存的URL。

以下是一个示例:

get("/{path}") {
    val userSession: UserSession? = call.sessions.get()
    if (userSession != null) {
        val userInfo: UserInfo = httpClient.get("https://www.googleapis.com/oauth2/v2/userinfo") {
            headers {
                append(HttpHeaders.Authorization, "Bearer ${userSession.token}")
            }
        }.body()
        call.respondText("Hello, ${userInfo.name}!")
    } else {
        val redirectUrl = URLBuilder("http://0.0.0.0:8080/login").run {
            parameters.append("redirectUrl", call.request.uri)
            build()
        }
        call.respondRedirect(redirectUrl)
    }
}

如有需要,您可以使用此示例代码来实现所描述的重定向逻辑。

英文:

If you need to redirect a client to the URL it came from, add the query parameter redirectUrl (the actual name doesn't matter but has to be consistent) to the /login endpoint. Here are the steps of the flow:

  1. Client requests an unprotected endpoint
  2. If the session is absent (the client is unauthenticated), the server responds with a redirect to /login with a query parameter redirectUrl, which contains the requested URL.
  3. The /login endpoint triggers OAuth2 authentication, and when a state is created, the redirectUrl is saved to the redirects map inside the onStateCreated block.
  4. The client is redirected by the OAuth2 provider to the callback URL, where the redirect is happening to the previously saved URL.

Here is an example:

get(&quot;/{path}&quot;) {
val userSession: UserSession? = call.sessions.get()
if (userSession != null) {
val userInfo: UserInfo = httpClient.get(&quot;https://www.googleapis.com/oauth2/v2/userinfo&quot;) {
headers {
append(HttpHeaders.Authorization, &quot;Bearer ${userSession.token}&quot;)
}
}.body()
call.respondText(&quot;Hello, ${userInfo.name}!&quot;)
} else {
val redirectUrl = URLBuilder(&quot;http://0.0.0.0:8080/login&quot;).run {
parameters.append(&quot;redirectUrl&quot;, call.request.uri)
build()
}
call.respondRedirect(redirectUrl)
}
}

答案2

得分: 1

我成功修复了它,只需将call.request.queryParameters["redirectUrl"]!!替换为普通字符串,如"http://localhost:3000/home"即可。

只要你要重定向的URL保持一致,它就能正常工作。

英文:

I was able to fix it by exchanging call.request.queryParameters[&quot;redirectUrl&quot;]!! with just a normal string, such as &quot;http://localhost:3000/home&quot;.

Works as long as you only have the same url you are going to redirect to.

答案3

得分: 0

答案提供的内容未解决问题。在使用ktor oauth模型示例时出现了NullPointerException。

另外,在get("/{path}") {}块中,当存在会话可用时,如何进行重定向?如果我尝试调用call.respondRedirect(),它会再次进入get("/{path}") {}块,这会导致无限循环。

英文:

The answer given is not addressing the problem. There is NullPointerException when the ktor oauth model example is used.

Also, in the get("/{path}") {} block, for the case when there is a existing session avilable, how do we redirect? If I try to call.respondRedirect(), it again lands into get("/{path}") {} block. This is infinite loop.

huangapple
  • 本文由 发表于 2023年2月27日 19:19:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/75579794.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定