Implemented logout

This commit is contained in:
2026-07-10 02:41:31 +03:00
parent 5aa6e30c90
commit 0f7925b56c
5 changed files with 62 additions and 4 deletions
@@ -0,0 +1,35 @@
package controllers.authentication
import dao.AuthTokensDAO
import jakarta.inject.Inject
import models.AuthToken
import play.api.libs.json.*
import play.api.mvc.Results.Unauthorized
import play.api.mvc.{ActionRefiner, Request, Result, WrappedRequest}
import scala.concurrent.{ExecutionContext, Future}
class AuthenticatedRequest[A](val token: AuthToken, request: Request[A]) extends WrappedRequest[A](request)
class AuthenticatedAction @Inject()(authTokensDao: AuthTokensDAO)(implicit val executionContext: ExecutionContext) extends ActionRefiner[Request, AuthenticatedRequest] {
private final val tokenPrefix = "Token"
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")
.flatMap { header =>
if header.startsWith(tokenPrefix) then Some(header.substring(tokenPrefix.length).trim)
else None
}
.filter(_.nonEmpty)
authHeader match {
case Some(token) =>
authTokensDao.find(token).map {
case Some(authToken) => Right(AuthenticatedRequest[A](authToken, request))
case None => unauthenticatedRequestError
}
case None => Future.successful(unauthenticatedRequestError)
}
}
}
@@ -40,4 +40,8 @@ class SessionsController @Inject()(controllerComponents: ControllerComponents, u
}
)
}
def logout = Action.andThen(new AuthenticatedAction(authTokensDAO)).async { request =>
authTokensDAO.destroy(request.token).map{ _ => Ok("") }
}
}
+15 -4
View File
@@ -3,24 +3,30 @@ package dao
import jakarta.inject.Inject
import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfigProvider}
import slick.jdbc.PostgresProfile
import slick.jdbc.PostgresProfile.api._
import slick.jdbc.PostgresProfile.api.*
import java.nio.ByteBuffer
import java.security.SecureRandom
import java.util.Base64
import java.time.{Instant, Duration}
import java.time.{Duration, Instant}
import scala.concurrent.{ExecutionContext, Future}
import models.AuthToken
import schemas.AuthTokens
import scala.language.postfixOps
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)
def find(token: String): Future[Option[AuthToken]] = {
val query = AuthTokens.filter(row => row.token === token && row.expiresAt > Instant.now()).take(1).result.headOption
db.run(query)
}
def createToken(user: models.User): Future[AuthToken] = {
val tokenRaw = ByteBuffer.allocate(10)
val tokenRaw = ByteBuffer.allocate(12)
random.nextBytes(tokenRaw.array())
val token = AuthToken(
id = None,
@@ -32,4 +38,9 @@ class AuthTokensDAO @Inject()(protected val dbConfigProvider: DatabaseConfigProv
db.run((AuthTokens returning AuthTokens.map(_.id)) += token).map { tokenId => token.copy(id = Some(tokenId)) }
}
def destroy(token: AuthToken): Future[Unit] = {
val q = AuthTokens.filter(_.token === token.token).delete
db.run(q).map{ _ => () }
}
}