Added User to AuthenticatedAction

This commit is contained in:
2026-08-25 23:37:40 +03:00
parent 9bfee2978e
commit 8582a92d85
4 changed files with 41 additions and 12 deletions
+3 -1
View File
@@ -7,4 +7,6 @@ hs_err_pid*
/project/project/target/ /project/project/target/
/project/target /project/target
/target /target
.bsp
@@ -2,20 +2,19 @@ package controllers.authentication
import dao.AuthTokensDAO import dao.AuthTokensDAO
import jakarta.inject.Inject import jakarta.inject.Inject
import models.AuthToken import models.{AuthToken, User}
import play.api.libs.json.* import play.api.libs.json.*
import play.api.mvc.Results.Unauthorized import play.api.mvc.Results.Unauthorized
import play.api.mvc.{ActionRefiner, Request, Result, WrappedRequest} import play.api.mvc.{ActionRefiner, Request, Result, WrappedRequest}
import scala.concurrent.{ExecutionContext, Future} import scala.concurrent.{ExecutionContext, Future}
class AuthenticatedRequest[A](val token: AuthToken, request: Request[A]) extends WrappedRequest[A](request) class AuthenticatedRequest[A](val token: AuthToken, val user: User, request: Request[A]) extends WrappedRequest[A](request)
class AuthenticatedAction @Inject()(authTokensDao: AuthTokensDAO)(implicit val executionContext: ExecutionContext) extends ActionRefiner[Request, AuthenticatedRequest] { class AuthenticatedAction @Inject()(authTokensDao: AuthTokensDAO)(implicit val executionContext: ExecutionContext) extends ActionRefiner[Request, AuthenticatedRequest] {
private final val tokenPrefix = "Token" private final val tokenPrefix = "Token"
override protected def refine[A](request: Request[A]): Future[Either[Result, AuthenticatedRequest[A]]] = { override protected def refine[A](request: Request[A]): Future[Either[Result, AuthenticatedRequest[A]]] = {
val unauthenticatedRequestError = Left(Unauthorized(Json.obj("error" -> "Unauthenticated")))
val authHeader = request.headers.get("Authorization") val authHeader = request.headers.get("Authorization")
.flatMap { header => .flatMap { header =>
if header.startsWith(tokenPrefix) then Some(header.substring(tokenPrefix.length).trim) if header.startsWith(tokenPrefix) then Some(header.substring(tokenPrefix.length).trim)
@@ -25,11 +24,21 @@ class AuthenticatedAction @Inject()(authTokensDao: AuthTokensDAO)(implicit val e
authHeader match { authHeader match {
case Some(token) => case Some(token) =>
authTokensDao.find(token).map { authTokensDao.find(token).flatMap {
case Some(authToken) => Right(AuthenticatedRequest[A](authToken, request)) case Some(authToken) => findUser(request, authToken)
case None => unauthenticatedRequestError case None => Future.successful(unauthenticatedRequestError)
} }
case None => Future.successful(unauthenticatedRequestError) case None => Future.successful(unauthenticatedRequestError)
} }
} }
private val unauthenticatedRequestError = Left(Unauthorized(Json.obj("error" -> "Unauthenticated")))
private def findUser[A](request: Request[A], token: AuthToken) = {
authTokensDao.getUser(token).map {
case Some(user) => Right(AuthenticatedRequest(token, user, request))
case None => unauthenticatedRequestError
}
}
} }
+7 -5
View File
@@ -12,12 +12,11 @@ import java.time.{Duration, Instant}
import scala.concurrent.{ExecutionContext, Future} import scala.concurrent.{ExecutionContext, Future}
import models.AuthToken import models.AuthToken
import schemas.AuthTokens import schemas.AuthTokens
import utils.Random
import scala.language.postfixOps import scala.language.postfixOps
class AuthTokensDAO @Inject()(protected val dbConfigProvider: DatabaseConfigProvider)(implicit val ec: ExecutionContext) extends HasDatabaseConfigProvider[PostgresProfile] { class AuthTokensDAO @Inject()(protected val dbConfigProvider: DatabaseConfigProvider)(implicit val ec: ExecutionContext) extends HasDatabaseConfigProvider[PostgresProfile] {
private lazy val random = SecureRandom()
private lazy val base64 = Base64.getEncoder
private final lazy val tokenValidDuration = Duration.ofDays(30) private final lazy val tokenValidDuration = Duration.ofDays(30)
def find(token: String): Future[Option[AuthToken]] = { def find(token: String): Future[Option[AuthToken]] = {
@@ -26,12 +25,10 @@ class AuthTokensDAO @Inject()(protected val dbConfigProvider: DatabaseConfigProv
} }
def createToken(user: models.User): Future[AuthToken] = { def createToken(user: models.User): Future[AuthToken] = {
val tokenRaw = ByteBuffer.allocate(12)
random.nextBytes(tokenRaw.array())
val token = AuthToken( val token = AuthToken(
id = None, id = None,
userId = user.id.get, userId = user.id.get,
token = base64.encodeToString(tokenRaw.array()), token = Random.randomBase64(12),
createdAt = Instant.now, createdAt = Instant.now,
expiresAt = Instant.now.plus(tokenValidDuration) expiresAt = Instant.now.plus(tokenValidDuration)
) )
@@ -39,6 +36,11 @@ class AuthTokensDAO @Inject()(protected val dbConfigProvider: DatabaseConfigProv
db.run((AuthTokens returning AuthTokens.map(_.id)) += token).map { tokenId => token.copy(id = Some(tokenId)) } db.run((AuthTokens returning AuthTokens.map(_.id)) += token).map { tokenId => token.copy(id = Some(tokenId)) }
} }
def getUser(token: AuthToken): Future[Option[models.User]] = {
val query = schemas.Users.filter(_.id === token.userId).take(1).result.headOption
db.run(query)
}
def destroy(token: AuthToken): Future[Unit] = { def destroy(token: AuthToken): Future[Unit] = {
val q = AuthTokens.filter(_.token === token.token).delete val q = AuthTokens.filter(_.token === token.token).delete
db.run(q).map{ _ => () } db.run(q).map{ _ => () }
+16
View File
@@ -0,0 +1,16 @@
package utils
import java.nio.ByteBuffer
import java.security.SecureRandom
import java.util.Base64
object Random {
private lazy val random = SecureRandom()
private lazy val base64 = Base64.getEncoder
def randomBase64(bytes: Int): String = {
val tokenRaw = ByteBuffer.allocate(bytes)
random.nextBytes(tokenRaw.array())
base64.encodeToString(tokenRaw.array())
}
}