move JWT secret into ENV #87

Merged
flecomte merged 1 commits from jwt-token-into-env into master 2021-03-31 18:23:13 +02:00
11 changed files with 63 additions and 17 deletions

View File

@@ -38,6 +38,9 @@ services:
REDIS_CONNECTION: ${REDIS_CONNECTION} REDIS_CONNECTION: ${REDIS_CONNECTION}
RABBITMQ_CONNECTION: ${RABBITMQ_CONNECTION} RABBITMQ_CONNECTION: ${RABBITMQ_CONNECTION}
ELASTICSEARCH_CONNECTION: ${ELASTICSEARCH_CONNECTION} ELASTICSEARCH_CONNECTION: ${ELASTICSEARCH_CONNECTION}
JWT_SECRET: ${JWT_SECRET}
JWT_ISSUER: ${JWT_ISSUER}
JWT_VALIDITY: ${JWT_VALIDITY}
depends_on: depends_on:
- elasticsearch - elasticsearch
- db - db

View File

@@ -124,7 +124,7 @@ fun Application.module(env: Env = PROD) {
} }
} }
install(Authentication, jwtInstallation(get())) install(Authentication, jwtInstallation(get(), get()))
install(AutoHeadResponse) install(AutoHeadResponse)

View File

@@ -43,4 +43,15 @@ class Configuration(val config: Config) {
val rabbitmq: String = config.getString("rabbitmq.connection") val rabbitmq: String = config.getString("rabbitmq.connection")
val exchangeNotificationName = "notification" val exchangeNotificationName = "notification"
val sendGridKey: String = config.getString("mail.sendGrid.key") val sendGridKey: String = config.getString("mail.sendGrid.key")
interface Jwt {
val secret: String
val issuer: String
val validityInMs: Int
}
val jwt = object : Jwt {
override val secret = config.getString("jwt.secret")
override val issuer = config.getString("jwt.issuer")
override val validityInMs = config.getInt("jwt.validity")
}
} }

View File

@@ -9,6 +9,7 @@ import com.fasterxml.jackson.datatype.joda.JodaModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.rabbitmq.client.ConnectionFactory import com.rabbitmq.client.ConnectionFactory
import fr.dcproject.common.email.Mailer import fr.dcproject.common.email.Mailer
import fr.dcproject.component.auth.jwt.JwtConfig
import fr.dcproject.component.notification.NotificationConsumer import fr.dcproject.component.notification.NotificationConsumer
import fr.dcproject.component.notification.NotificationEmailSender import fr.dcproject.component.notification.NotificationEmailSender
import fr.dcproject.component.notification.NotificationsPush import fr.dcproject.component.notification.NotificationsPush
@@ -25,6 +26,19 @@ import org.koin.dsl.module
@KtorExperimentalAPI @KtorExperimentalAPI
val KoinModule = module { val KoinModule = module {
// JWT
single {
val config: Configuration = get()
JwtConfig(
config.jwt.secret,
config.jwt.issuer,
config.jwt.validityInMs,
)
}
// JWT Verifier
single {
get<JwtConfig>().verifier
}
// SQL connection // SQL connection
single { single {
val config: Configuration = get() val config: Configuration = get()

View File

@@ -2,13 +2,16 @@ package fr.dcproject.component.auth.jwt
import com.auth0.jwt.JWT import com.auth0.jwt.JWT
import fr.dcproject.component.auth.database.UserI import fr.dcproject.component.auth.database.UserI
import org.koin.core.context.GlobalContext
/** /**
* Produce a token for this combination of User and Account * Produce a token for this combination of User and Account
*/ */
fun UserI.makeToken(): String = JWT.create() fun UserI.makeToken(): String = GlobalContext.get().koin.get<JwtConfig>().run {
.withSubject("Authentication") JWT.create()
.withIssuer(JwtConfig.issuer) .withSubject("Authentication")
.withClaim("id", id.toString()) .withIssuer(issuer)
.withExpiresAt(JwtConfig.getExpiration()) .withClaim("id", id.toString())
.sign(JwtConfig.algorithm) .withExpiresAt(getExpiration())
.sign(algorithm)
}

View File

@@ -5,11 +5,11 @@ import com.auth0.jwt.JWTVerifier
import com.auth0.jwt.algorithms.Algorithm import com.auth0.jwt.algorithms.Algorithm
import java.util.Date import java.util.Date
object JwtConfig { class JwtConfig(
private const val secret = "zAP5MBA4B4Ijz0MZaS48" private val secret: String,
const val issuer = "dc-project.fr" val issuer: String,
private const val validityInMs = 3_600_000 * 10 // 10 hours private val validityInMs: Int,
) {
// TODO change to RSA512 // TODO change to RSA512
val algorithm: Algorithm = Algorithm.HMAC512(secret) val algorithm: Algorithm = Algorithm.HMAC512(secret)

View File

@@ -1,5 +1,6 @@
package fr.dcproject.component.auth.jwt package fr.dcproject.component.auth.jwt
import com.auth0.jwt.JWTVerifier
import fr.dcproject.component.auth.database.User import fr.dcproject.component.auth.database.User
import fr.dcproject.component.auth.database.UserRepository import fr.dcproject.component.auth.database.UserRepository
import io.ktor.application.ApplicationCall import io.ktor.application.ApplicationCall
@@ -9,14 +10,14 @@ import io.ktor.http.auth.HttpAuthHeader
import io.ktor.routing.Routing import io.ktor.routing.Routing
import java.util.UUID import java.util.UUID
fun jwtInstallation(userRepo: UserRepository): Authentication.Configuration.() -> Unit = { fun jwtInstallation(userRepo: UserRepository, verifier: JWTVerifier): Authentication.Configuration.() -> Unit = {
/** /**
* Setup the JWT authentication to be used in [Routing]. * Setup the JWT authentication to be used in [Routing].
* If the token is valid, the corresponding [User] is fetched from the database. * If the token is valid, the corresponding [User] is fetched from the database.
* The [User] can then be accessed in each [ApplicationCall]. * The [User] can then be accessed in each [ApplicationCall].
*/ */
jwt { jwt {
verifier(JwtConfig.verifier) verifier(verifier)
realm = "dc-project.fr" realm = "dc-project.fr"
validate { validate {
it.payload.getClaim("id").asString()?.let { id -> it.payload.getClaim("id").asString()?.let { id ->
@@ -27,7 +28,7 @@ fun jwtInstallation(userRepo: UserRepository): Authentication.Configuration.() -
/* Token in URL */ /* Token in URL */
jwt("url") { jwt("url") {
verifier(JwtConfig.verifier) verifier(verifier)
realm = "dc-project.fr" realm = "dc-project.fr"
authHeader { call -> authHeader { call ->
call.request.queryParameters["token"]?.let { call.request.queryParameters["token"]?.let {

View File

@@ -42,3 +42,11 @@ mail {
key = ${?SEND_GRID_KEY} key = ${?SEND_GRID_KEY}
} }
} }
jwt {
secret = ${?JWT_SECRET}
issuer = "dc-project.fr"
issuer = ${?JWT_ISSUER}
validity = 36000000
validity = ${?JWT_VALIDITY}
}

View File

@@ -144,5 +144,4 @@ class `Check auth on all routes` : BaseTest() {
listOf("example123") listOf("example123")
} }
} }
} }

View File

@@ -15,10 +15,11 @@ fun TestApplicationRequest.`authenticated as`(
val username = "$firstName-$lastName".toLowerCase() val username = "$firstName-$lastName".toLowerCase()
val repo: CitizenRepository by lazy<CitizenRepository> { GlobalContext.get().koin.get() } val repo: CitizenRepository by lazy<CitizenRepository> { GlobalContext.get().koin.get() }
val citizen = repo.findByUsername(username) ?: error("Citizen not exist with username $username") val citizen = repo.findByUsername(username) ?: error("Citizen not exist with username $username")
val algorithm = GlobalContext.get().koin.get<JwtConfig>().algorithm
val jwtAsString: String = JWT.create() val jwtAsString: String = JWT.create()
.withIssuer("dc-project.fr") .withIssuer("dc-project.fr")
.withClaim("id", citizen.user.id.toString()) .withClaim("id", citizen.user.id.toString())
.sign(JwtConfig.algorithm) .sign(algorithm)
addHeader(HttpHeaders.Authorization, "Bearer $jwtAsString") addHeader(HttpHeaders.Authorization, "Bearer $jwtAsString")

View File

@@ -37,3 +37,9 @@ mail {
key = "abcd" key = "abcd"
} }
} }
jwt {
secret = "zAP5MBA4B4Ijz0MZaS48"
issuer = "dc-project.fr"
validity = 36000000
}