package controllers.authentication import dao.{AuthTokensDAO, UsersDAO} import jakarta.inject.{Inject, Singleton} import models.AuthToken import play.api.libs.json.* import play.api.libs.functional.syntax.* import play.api.mvc.{AbstractController, ControllerComponents} import scala.concurrent.{ExecutionContext, Future} case class LoginData(username: String, password: String) implicit val loginDataReads: Reads[LoginData] = ( (JsPath \ "username").read[String] and (JsPath \ "password").read[String] )(LoginData.apply) implicit val tokenWrites: Writes[AuthToken] = (token: AuthToken) => Json.obj( "token" -> token.token, "createdAt" -> token.createdAt, "expiresAt" -> token.expiresAt ) @Singleton class SessionsController @Inject()(controllerComponents: ControllerComponents, usersDAO: UsersDAO, authTokensDAO: AuthTokensDAO, implicit val ec: ExecutionContext) extends AbstractController(controllerComponents) { def login() = Action.async(parse.json) { implicit request => val loginDataResult = request.body.validate[LoginData] loginDataResult.fold( errors => { Future.successful(BadRequest(Json.obj("message" -> JsError.toJson(errors)))) }, loginData => { usersDAO.authenticate(loginData.username, loginData.password).flatMap { userOption => userOption.fold( Future.successful(UnprocessableEntity(Json.obj("message" -> "Invalid authentication"))) ){ user => authTokensDAO.createToken(user).map(token => Ok(Json.toJson(token))) } } } ) } def logout = Action.andThen(new AuthenticatedAction(authTokensDAO)).async { request => authTokensDAO.destroy(request.token).map{ _ => Ok("") } } }