diff --git a/api/pom.xml b/api/pom.xml index ef64834..32470b9 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -16,6 +16,7 @@ 2.6.0 6.5.4 6.3.3 + 3.10.2 official UTF-8 @@ -79,6 +80,11 @@ ktor-jackson ${ktor_version} + + io.ktor + ktor-auth-jwt + ${ktor_version} + io.ktor ktor-client-core diff --git a/api/resources/application.conf b/api/resources/application.conf index 04a5dd7..4b934ce 100644 --- a/api/resources/application.conf +++ b/api/resources/application.conf @@ -14,4 +14,9 @@ database { name = "Notes" user = "test" password = "test" +} + +jwt { + secret = "thisisasecret" + secret = ${?SECRET} } \ No newline at end of file diff --git a/api/src/auth/AuthenticationModule.kt b/api/src/auth/AuthenticationModule.kt new file mode 100644 index 0000000..1a9de0f --- /dev/null +++ b/api/src/auth/AuthenticationModule.kt @@ -0,0 +1,21 @@ +package be.vandewalleh.auth + +import be.vandewalleh.kodein +import io.ktor.application.Application +import io.ktor.application.install +import io.ktor.auth.Authentication +import io.ktor.auth.UserIdPrincipal +import io.ktor.auth.jwt.jwt +import org.kodein.di.generic.instance + +fun Application.authenticationModule() { + install(Authentication) { + jwt { + val simpleJwt: SimpleJWT by kodein.instance() + verifier(simpleJwt.verifier) + validate { + UserIdPrincipal(it.payload.getClaim("name").asString()) + } + } + } +} \ No newline at end of file diff --git a/api/src/auth/SimpleJWT.kt b/api/src/auth/SimpleJWT.kt new file mode 100644 index 0000000..173a5c1 --- /dev/null +++ b/api/src/auth/SimpleJWT.kt @@ -0,0 +1,19 @@ +package be.vandewalleh.auth + +import com.auth0.jwt.JWT +import com.auth0.jwt.JWTVerifier +import com.auth0.jwt.algorithms.Algorithm +import java.util.* + +class SimpleJWT(secret: String) { + private val validityInMs = 36_000_00 * 1 + private val algorithm = Algorithm.HMAC256(secret) + + val verifier: JWTVerifier = JWT.require(algorithm).build() + fun sign(name: String): String = JWT.create() + .withClaim("name", name) + .withExpiresAt(getExpiration()) + .sign(algorithm) + + private fun getExpiration() = Date(System.currentTimeMillis() + validityInMs) +} \ No newline at end of file diff --git a/api/src/features/ConfigurationFeature.kt b/api/src/features/ConfigurationFeature.kt index 9dc02d2..4a2f0ad 100644 --- a/api/src/features/ConfigurationFeature.kt +++ b/api/src/features/ConfigurationFeature.kt @@ -1,5 +1,6 @@ package be.vandewalleh.features +import be.vandewalleh.auth.SimpleJWT import io.ktor.application.Application import org.kodein.di.Kodein import org.kodein.di.generic.bind @@ -27,7 +28,11 @@ fun Application.configurationFeature() { setPassword(password) } } + + val simpleJwt = SimpleJWT(environment.config.property("jwt.secret").getString()) + configurationModule = Kodein.Module("Configuration") { bind() with instance(dataSource) + bind() with instance(simpleJwt) } } \ No newline at end of file diff --git a/api/src/features/Features.kt b/api/src/features/Features.kt index 3003579..b594a0f 100644 --- a/api/src/features/Features.kt +++ b/api/src/features/Features.kt @@ -1,14 +1,18 @@ package be.vandewalleh.features +import be.vandewalleh.auth.authenticationModule import io.ktor.application.Application import org.kodein.di.Kodein import org.kodein.di.KodeinAware fun Application.features() { + // must be first to be loaded + configurationFeature() + locationFeature() corsFeature() contentNegotiationFeature() - configurationFeature() + authenticationModule() } abstract class Feature(override val kodein: Kodein) : KodeinAware {