From aad86a31a56efc6a151dc751a9cfe1b1b0ed854b Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 30 Apr 2023 17:05:10 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=82=B5=E3=83=B3=E3=83=97=E3=83=AB?= =?UTF-8?q?=E3=81=AE=E5=80=A4=E3=82=92=E4=BD=BF=E3=81=A3=E3=81=A6JWT?= =?UTF-8?q?=E3=81=A7=E3=83=AD=E3=82=B0=E3=82=A4=E3=83=B3=E3=83=88=E3=83=BC?= =?UTF-8?q?=E3=82=AF=E3=83=B3=E3=82=92=E7=99=BA=E8=A1=8C=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/dev/usbharu/hideout/Application.kt | 1 + .../domain/model/hideout/form/UserLogin.kt | 3 + .../dev/usbharu/hideout/plugins/Security.kt | 82 ++++++++++++++++--- .../usbharu/hideout/routing/LoginRouting.kt | 8 ++ src/main/resources/application.conf | 7 ++ 5 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/form/UserLogin.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/routing/LoginRouting.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/Application.kt b/src/main/kotlin/dev/usbharu/hideout/Application.kt index de723c6b..9fb82629 100644 --- a/src/main/kotlin/dev/usbharu/hideout/Application.kt +++ b/src/main/kotlin/dev/usbharu/hideout/Application.kt @@ -97,6 +97,7 @@ fun Application.parent() { configureMonitoring() configureSerialization() register(inject().value) + configureSecurity(inject().value) configureRouting( inject().value, inject().value, diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/form/UserLogin.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/form/UserLogin.kt new file mode 100644 index 00000000..d9d5fc4d --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/form/UserLogin.kt @@ -0,0 +1,3 @@ +package dev.usbharu.hideout.domain.model.hideout.form + +data class UserLogin(val username: String, val password: String) diff --git a/src/main/kotlin/dev/usbharu/hideout/plugins/Security.kt b/src/main/kotlin/dev/usbharu/hideout/plugins/Security.kt index da66a042..e5a3766f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/plugins/Security.kt +++ b/src/main/kotlin/dev/usbharu/hideout/plugins/Security.kt @@ -2,24 +2,86 @@ package dev.usbharu.hideout.plugins +import com.auth0.jwk.JwkProviderBuilder +import com.auth0.jwt.JWT +import com.auth0.jwt.algorithms.Algorithm +import dev.usbharu.hideout.domain.model.hideout.form.UserLogin +import dev.usbharu.hideout.property import dev.usbharu.hideout.service.IUserAuthService +import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.auth.* +import io.ktor.server.auth.jwt.* +import io.ktor.server.request.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import java.security.KeyFactory +import java.security.interfaces.RSAPrivateKey +import java.security.interfaces.RSAPublicKey +import java.security.spec.PKCS8EncodedKeySpec +import java.util.* +import java.util.concurrent.TimeUnit -const val TOKEN_AUTH = "token-auth" +const val TOKEN_AUTH = "jwt-auth" fun Application.configureSecurity(userAuthService: IUserAuthService) { + + val privateKeyString = property("jwt.privateKey") + val issuer = property("jwt.issuer") +// val audience = property("jwt.audience") + val myRealm = property("jwt.realm") + val jwkProvider = JwkProviderBuilder(issuer) + .cached(10, 24, TimeUnit.HOURS) + .rateLimited(10, 1, TimeUnit.MINUTES) + .build() install(Authentication) { - bearer(TOKEN_AUTH) { - authenticate { bearerTokenCredential -> - UserIdPrincipal(bearerTokenCredential.token) + jwt(TOKEN_AUTH) { + realm = myRealm + verifier(jwkProvider, issuer) { + acceptLeeway(3) + } + validate { jwtCredential -> + if (jwtCredential.payload.getClaim("username").asString().isNotEmpty()) { + JWTPrincipal(jwtCredential.payload) + } else { + null + } } - skipWhen { true } } } -// install(Sessions) { -// cookie("MY_SESSION") { -// cookie.extensions["SameSite"] = "lax" -// } -// } + + routing { + post("/login") { + val user = call.receive() + val check = userAuthService.verifyAccount(user.username, user.password) + if (check.not()) { + return@post call.respond(HttpStatusCode.Unauthorized) + } + + val publicKey = jwkProvider.get("6f8856ed-9189-488f-9011-0ff4b6c08edc").publicKey + val keySpecPKCS8 = PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyString)) + val privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpecPKCS8) + val token = JWT.create() +// .withAudience(audience) +// .withIssuer(issuer) + .withClaim("username", user.username) + .withExpiresAt(Date(System.currentTimeMillis() + 60000)) + .sign(Algorithm.RSA256(publicKey as RSAPublicKey, privateKey as RSAPrivateKey)) + return@post call.respond(hashSetOf("token" to token)) + } + + get("/.well-known/jwks.json"){ + //language=JSON + call.respondText(contentType = ContentType.Application.Json,text = """{ + "keys": [ + { + "kty": "RSA", + "e": "AQAB", + "kid": "6f8856ed-9189-488f-9011-0ff4b6c08edc", + "n":"tfJaLrzXILUg1U3N1KV8yJr92GHn5OtYZR7qWk1Mc4cy4JGjklYup7weMjBD9f3bBVoIsiUVX6xNcYIr0Ie0AQ" + } + ] +}""") + } + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/routing/LoginRouting.kt b/src/main/kotlin/dev/usbharu/hideout/routing/LoginRouting.kt new file mode 100644 index 00000000..e0db266b --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/routing/LoginRouting.kt @@ -0,0 +1,8 @@ +package dev.usbharu.hideout.routing + +import dev.usbharu.hideout.service.IUserAuthService +import io.ktor.server.routing.* + +fun Routing.login(userAuthService: IUserAuthService){ + +} diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index beb8fa3b..db3cc28b 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -20,3 +20,10 @@ hideout { password = "" } } + +jwt { + privateKey = "MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAtfJaLrzXILUg1U3N1KV8yJr92GHn5OtYZR7qWk1Mc4cy4JGjklYup7weMjBD9f3bBVoIsiUVX6xNcYIr0Ie0AQIDAQABAkEAg+FBquToDeYcAWBe1EaLVyC45HG60zwfG1S4S3IB+y4INz1FHuZppDjBh09jptQNd+kSMlG1LkAc/3znKTPJ7QIhANpyB0OfTK44lpH4ScJmCxjZV52mIrQcmnS3QzkxWQCDAiEA1Tn7qyoh+0rOO/9vJHP8U/beo51SiQMw0880a1UaiisCIQDNwY46EbhGeiLJR1cidr+JHl86rRwPDsolmeEF5AdzRQIgK3KXL3d0WSoS//K6iOkBX3KMRzaFXNnDl0U/XyeGMuUCIHaXv+n+Brz5BDnRbWS+2vkgIe9bUNlkiArpjWvX+2we" + issuer = "http://0.0.0.0:8080/" + audience = "http://0.0.0.0:8080/hello" + realm = "Access to 'hello'" +}