add OAuth flows

- add naver OAuth flow
This commit is contained in:
dalbodeule 2024-08-08 14:30:26 +09:00
parent 2f5c4293c0
commit a828e23767
No known key found for this signature in database
GPG Key ID: EFA860D069C9FA65
4 changed files with 119 additions and 3 deletions

View File

@ -6,5 +6,9 @@ DB_PASS=chzzk
RUN_AGENT=false
YOUTUBE_API_KEY=
RAPID_KEY=
HOST=http://localhost:8080
FRONTEND=http://localhost:3000
NAVER_CLIENT_ID=
NAVER_CLIENT_SECRET=
NID_AUT=
NID_SES=

View File

@ -18,9 +18,14 @@ dependencies {
implementation("io.ktor:ktor-server-websockets:$ktorVersion")
implementation("io.ktor:ktor-server-swagger:$ktorVersion")
implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
implementation("io.ktor:ktor-server-cors:$ktorVersion")
implementation("io.ktor:ktor-server-swagger:$ktorVersion")
implementation("io.ktor:ktor-server-auth:$ktorVersion")
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-cio:$ktorVersion")
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
implementation("io.swagger.codegen.v3:swagger-codegen-generators:1.0.50")
@ -35,6 +40,9 @@ dependencies {
// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
implementation("ch.qos.logback:logback-classic:1.5.6")
// https://mvnrepository.com/artifact/io.github.cdimascio/dotenv-kotlin
implementation("io.github.cdimascio:dotenv-kotlin:6.4.1")
implementation(project(":common"))
testImplementation(kotlin("test"))

View File

@ -1,21 +1,38 @@
package space.mori.chzzk_bot.webserver
import applicationHttpClient
import io.github.cdimascio.dotenv.dotenv
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.cors.routing.*
import io.ktor.server.plugins.swagger.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.sessions.*
import io.ktor.server.websocket.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import space.mori.chzzk_bot.common.dotenv
import space.mori.chzzk_bot.webserver.routes.*
import java.time.Duration
val server = embeddedServer(Netty, port = 8080) {
val dotenv = dotenv {
ignoreIfMissing = true
}
const val naverMeAPIURL = "https://openapi.naver.com/v1/nid/me"
val redirects = mutableMapOf<String, String>()
val server = embeddedServer(Netty, port = 8080, ) {
install(WebSockets) {
pingPeriod = Duration.ofSeconds(15)
timeout = Duration.ofSeconds(15)
@ -34,7 +51,66 @@ val server = embeddedServer(Netty, port = 8080) {
anyHost()
allowHeader(HttpHeaders.ContentType)
}
install(Sessions) {
cookie<UserSession>("user_session", storage = SessionStorageMemory()) {}
}
install(Authentication) {
oauth("auth-oauth-naver") {
urlProvider = { "${dotenv["HOST"]}/auth/callback" }
providerLookup = { OAuthServerSettings.OAuth2ServerSettings(
name = "naver",
authorizeUrl = "https://nid.naver.com/oauth2.0/authorize",
accessTokenUrl = "https://nid.naver.com/oauth2.0/token",
requestMethod = HttpMethod.Post,
clientId = dotenv["NAVER_CLIENT_ID"],
clientSecret = dotenv["NAVER_CLIENT_SECRET"],
defaultScopes = listOf(""),
extraAuthParameters = listOf(),
onStateCreated = { call, state ->
//saves new state with redirect url value
call.request.queryParameters["redirectUrl"]?.let {
redirects[state] = it
}
}
)}
client = applicationHttpClient
}
}
routing {
route("/auth") {
authenticate("auth-oauth-naver") {
get("/login") {
}
get("/callback") {
val currentPrincipal = call.principal<OAuthAccessTokenResponse.OAuth2>()
currentPrincipal?.let { principal ->
principal.state?.let { state ->
val userInfo: NaverAPI<NaverMeAPI> = applicationHttpClient.get(naverMeAPIURL) {
headers{
append(HttpHeaders.Authorization, "Bearer ${principal.accessToken}")
}
}.body()
call.sessions.set(userInfo.response?.let { it1 ->
UserSession(state,
it1.id, it1.nickname, it1.profile_image)
})
redirects[state]?.let { redirect ->
call.respondRedirect(redirect)
return@get
}
}
}
call.respondRedirect(dotenv["FRONTEND"])
}
}
get("/logout") {
call.sessions.clear<UserSession>()
}
}
apiRoutes()
apiSongRoutes()
wsTimerRoutes()
@ -54,4 +130,22 @@ fun start() {
fun stop() {
server.stop()
}
}
@Serializable
data class UserSession(
val state: String,
val id: String,
val nickname: String,
val profileImage: String
)
@Serializable
data class NaverMeAPI(
val id: String,
val nickname: String,
val profile_image: String
)
@Serializable
data class NaverAPI<T>(val resultcode: String, val message: String, val response: T?)

View File

@ -0,0 +1,10 @@
import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
val applicationHttpClient = HttpClient(CIO) {
install(ContentNegotiation) {
json()
}
}