feat(user-sessions): add JWT-based session management and main menu route

This commit is contained in:
2025-10-29 10:31:49 +01:00
parent 306d5b309b
commit b6f0089d64
9 changed files with 138 additions and 6 deletions

View File

@@ -24,13 +24,31 @@ import javax.inject.*
@Singleton
class UserController @Inject()(val controllerComponents: ControllerComponents, val sessionManager: SessionManager, val userManager: UserManager) extends BaseController {
def mainMenu() : Action[AnyContent] = {
Action { implicit request =>
val session = request.cookies.get("sessionId")
if (session.isDefined) {
val possibleUser = sessionManager.getUserBySession(session.get.value)
if (possibleUser.isDefined) {
Ok("Main Menu for user: " + possibleUser.get.name)
} else
{
println("Invalid session, redirecting to login")
Redirect("/login")
}
} else {
Redirect("/login")
}
}
}
def login(): Action[AnyContent] = {
Action { implicit request =>
val session = request.cookies.get("sessionId")
if (session.isDefined) {
val possibleUser = sessionManager.getUserBySession(session.get.value)
if (possibleUser.isDefined) {
Redirect("/main-menu")
Redirect("/mainmenu")
} else
{
Ok(views.html.login())
@@ -54,6 +72,7 @@ class UserController @Inject()(val controllerComponents: ControllerComponents, v
Cookie("sessionId", sessionManager.createSession(possibleUser.get))
)
} else {
println("Failed login attempt for user: " + username)
Unauthorized("Invalid username or password")
}
} else {

View File

@@ -1,18 +1,27 @@
package logic.user.impl
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import com.typesafe.config.Config
import logic.user.SessionManager
import model.users.User
import services.JwtKeyProvider
import javax.inject.{Inject, Singleton}
@Singleton
class BaseSessionManager @Inject()(val config: Config) extends SessionManager {
class BaseSessionManager @Inject()(val keyProvider: JwtKeyProvider, val config: Config) extends SessionManager {
private val algorithm = Algorithm.RSA512(keyProvider.publicKey, keyProvider.privateKey)
override def createSession(user: User): String = {
//TODO create JWT token instead of random string
//Write session identifier to cache and DB
val sessionId = java.util.UUID.randomUUID().toString
val sessionId = JWT.create()
.withIssuer(config.getString("auth.issuer"))
.withAudience(config.getString("auth.audience"))
.withSubject(user.internalId.toString)
.sign(algorithm)
//TODO write to DB
sessionId
}

View File

@@ -0,0 +1,56 @@
package services
import java.nio.file.{Files, Paths}
import java.security.{KeyFactory, PrivateKey, PublicKey}
import java.security.spec.X509EncodedKeySpec
import java.util.Base64
import javax.inject.*
import play.api.Configuration
import java.security.interfaces.{RSAPrivateKey, RSAPublicKey}
@Singleton
class JwtKeyProvider @Inject()(config: Configuration) {
private def cleanPem(pem: String): String =
pem.replaceAll("-----BEGIN (.*)-----", "")
.replaceAll("-----END (.*)-----", "")
.replaceAll("\\s", "")
private def loadPublicKeyFromPem(pem: String): RSAPublicKey = {
val decoded = Base64.getDecoder.decode(cleanPem(pem))
val spec = new X509EncodedKeySpec(decoded)
KeyFactory.getInstance("RSA").generatePublic(spec).asInstanceOf[RSAPublicKey]
}
private def loadPrivateKeyFromPem(pem: String): RSAPrivateKey = {
val decoded = Base64.getDecoder.decode(cleanPem(pem))
val spec = new X509EncodedKeySpec(decoded)
KeyFactory.getInstance("RSA").generatePrivate(spec).asInstanceOf[RSAPrivateKey]
}
val publicKey: RSAPublicKey = {
val pemOpt = config.getOptional[String]("auth.publicKeyPem")
val fileOpt = config.getOptional[String]("auth.publicKeyFile")
pemOpt.orElse(fileOpt.map { path =>
new String(Files.readAllBytes(Paths.get(path)))
}) match {
case Some(pem) => loadPublicKeyFromPem(pem)
case None => throw new RuntimeException("No RSA public key configured.")
}
}
val privateKey: RSAPrivateKey = {
val pemOpt = config.getOptional[String]("auth.privateKeyPem")
val fileOpt = config.getOptional[String]("auth.privateKeyFile")
pemOpt.orElse(fileOpt.map { path =>
new String(Files.readAllBytes(Paths.get(path)))
}) match {
case Some(pem) => loadPrivateKeyFromPem(pem)
case None => throw new RuntimeException("No RSA private key configured.")
}
}
}