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'" +}