diff --git a/build.sbt b/build.sbt index a0dfba0..75e496e 100644 --- a/build.sbt +++ b/build.sbt @@ -13,6 +13,7 @@ val logbackVersion = "1.4.11" val vaultVersion = "3.6.0" val doobieVersion = "1.0.0-RC5" val postgresqlVersion = "42.7.4" +val bcryptVersion = "0.10.2" lazy val root = (project in file(".")) @@ -44,6 +45,9 @@ lazy val root = (project in file(".")) "org.tpolecat" %% "doobie-core" % doobieVersion, "org.tpolecat" %% "doobie-hikari" % doobieVersion, "org.tpolecat" %% "doobie-postgres" % doobieVersion, - "org.postgresql" % "postgresql" % postgresqlVersion + "org.postgresql" % "postgresql" % postgresqlVersion, + + // Bcrypt + "at.favre.lib" % "bcrypt" % bcryptVersion ) ) diff --git a/src/main/scala/org/yobble/scala_monolith/api/endpoint/auth/AuthEndpoints.scala b/src/main/scala/org/yobble/scala_monolith/api/endpoint/auth/AuthEndpoints.scala index bbb74ed..37dee02 100644 --- a/src/main/scala/org/yobble/scala_monolith/api/endpoint/auth/AuthEndpoints.scala +++ b/src/main/scala/org/yobble/scala_monolith/api/endpoint/auth/AuthEndpoints.scala @@ -14,6 +14,7 @@ object AuthEndpoints { val loginEndpoint: PublicEndpoint[LoginRequest, ErrorResponse, LoginResponse, Any] = endpoint.post .in("auth" / "login") + .tags(List("Auth")) .in(jsonBody[LoginRequest]) .out(jsonBody[LoginResponse]) .errorOut( diff --git a/src/main/scala/org/yobble/scala_monolith/service/AuthService.scala b/src/main/scala/org/yobble/scala_monolith/service/AuthService.scala index 1fb11a3..20484a1 100644 --- a/src/main/scala/org/yobble/scala_monolith/service/AuthService.scala +++ b/src/main/scala/org/yobble/scala_monolith/service/AuthService.scala @@ -1,5 +1,6 @@ package org.yobble.scala_monolith.service +import at.favre.lib.crypto.bcrypt.BCrypt import cats.effect.IO import org.yobble.scala_monolith.api.dto.{LoginRequest, LoginResponse} import org.yobble.scala_monolith.api.response.ErrorResponse @@ -10,16 +11,18 @@ class AuthService(userRepository: UserRepository) { def login(request: LoginRequest): IO[Either[ErrorResponse, LoginResponse]] = { userRepository.findByLogin(request.login).map { - case Some(user) if user.passwordHash != request.password => - Left(ErrorUtils.unauthorized("Invalid login or password")) - case Some(user) if user.isBlocked => - Left(ErrorUtils.forbidden("User account is disabled")) - case Some(user) if user.isDeleted => - Left(ErrorUtils.forbidden("User account is deleted")) case Some(user) => - // TODO: Implement proper password hashing (e.g., with bcrypt) - // TODO: Implement real token generation - Right(LoginResponse(accessToken = "fake-access-token", refreshToken = "fake-refresh-token")) + val passwordMatches = BCrypt.verifyer().verify(request.password.toCharArray, user.passwordHash).verified + if (!passwordMatches) { + Left(ErrorUtils.unauthorized("Invalid login or password")) + } else if (user.isBlocked) { + Left(ErrorUtils.forbidden("User account is disabled")) + } else if (user.isDeleted) { + Left(ErrorUtils.forbidden("User account is deleted")) + } else { + // TODO: Implement real token generation + Right(LoginResponse(accessToken = "fake-access-token", refreshToken = "fake-refresh-token")) + } case None => Left(ErrorUtils.unauthorized("Invalid login or password")) }