feat: set session token, improved paths

This commit is contained in:
Jean Jacques Avril 2025-01-04 11:27:08 +00:00
parent 89422726bf
commit 02af66b585
No known key found for this signature in database
4 changed files with 54 additions and 23 deletions

View File

@ -22,7 +22,7 @@ class AuthService {
/// Route to generate a token /// Route to generate a token
@Route.post('/login') @Route.post('/login')
Future<Response> generateToken(Request request) async { Future<Response> login(Request request) async {
return requestToJson(request) return requestToJson(request)
.flatMap(validateJsonKeys(['email', 'password'])) .flatMap(validateJsonKeys(['email', 'password']))
.flatMap((json) => decodeJson(json, LoginRequestDTO.fromJson)) .flatMap((json) => decodeJson(json, LoginRequestDTO.fromJson))
@ -59,8 +59,8 @@ class AuthService {
} }
/// Route to revoke a token /// Route to revoke a token
@Route.post('/revoke') @Route.post('/logout')
Future<Response> revokeToken(Request request) async { Future<Response> logout(Request request) async {
return requestToJson(request) return requestToJson(request)
.flatMap(validateJsonKeys(['token'])) .flatMap(validateJsonKeys(['token']))
.flatMap((json) => decodeJson(json, TokenRequestDTO.fromJson)) .flatMap((json) => decodeJson(json, TokenRequestDTO.fromJson))

View File

@ -11,7 +11,7 @@ Router _$AuthServiceRouter(AuthService service) {
router.add( router.add(
'POST', 'POST',
r'/login', r'/login',
service.generateToken, service.login,
); );
router.add( router.add(
'POST', 'POST',
@ -20,8 +20,8 @@ Router _$AuthServiceRouter(AuthService service) {
); );
router.add( router.add(
'POST', 'POST',
r'/revoke', r'/logout',
service.revokeToken, service.logout,
); );
return router; return router;
} }

View File

@ -2,6 +2,7 @@ package services
import ( import (
"actatempus_backend/internal/application/services/dto" "actatempus_backend/internal/application/services/dto"
mappers "actatempus_backend/internal/application/services/mapper"
"actatempus_backend/internal/domain/app_error" "actatempus_backend/internal/domain/app_error"
"actatempus_backend/internal/domain/entities" "actatempus_backend/internal/domain/entities"
"actatempus_backend/internal/domain/repository" "actatempus_backend/internal/domain/repository"
@ -32,7 +33,7 @@ func NewAuthService(authRepo repository.AuthRepository,
func (s *AuthService) RegisterRoutes(router *gin.RouterGroup) { func (s *AuthService) RegisterRoutes(router *gin.RouterGroup) {
router.POST("/login", s.Login) router.POST("/login", s.Login)
router.POST("/validate", s.Validate) router.POST("/validate", s.Validate)
router.POST("/revoke", s.Revoke) router.POST("/logout", s.Logout)
} }
// Login handles user login and token generation. // Login handles user login and token generation.
@ -47,24 +48,14 @@ func (s *AuthService) Login(c *gin.Context) {
loginRequest.Email, loginRequest.Email,
s.userRepository.FindByEmail(c.Request.Context()), s.userRepository.FindByEmail(c.Request.Context()),
E.Chain[error]( E.Chain[error](
func(user entities.User) E.Either[error, entities.User] { validatePassword(loginRequest.Password),
hashedPassword := data.GenerateSecureHash(loginRequest.Password)
if user.Password != hashedPassword {
return E.Left[entities.User, error](app_error.NewAuthError("Invalid password"))
}
return E.Right[error](user)
},
), ),
E.Chain(func(user entities.User) E.Either[error, dto.TokenResponseDTO] { E.Chain(func(user entities.User) E.Either[error, dto.TokenResponseDTO] {
return F.Pipe2( return F.Pipe3(
user.ID, user.ID,
s.authRepository.GenerateToken(c.Request.Context()), s.authRepository.GenerateToken(c.Request.Context()),
E.Map[error](func(token string) dto.TokenResponseDTO { E.Map[error](setSessionTokenCookie(c, user)),
return dto.TokenResponseDTO{ E.Map[error](mappers.ToTokenDTO(user)),
Token: token,
UserID: user.ID,
}
}),
) )
}), }),
E.Fold( E.Fold(
@ -97,14 +88,16 @@ func (s *AuthService) Validate(c *gin.Context) {
) )
} }
// Revoke handles token revocation. // Logout and token revocation.
func (s *AuthService) Revoke(c *gin.Context) { func (s *AuthService) Logout(c *gin.Context) {
var tokenRequest dto.TokenRequestDTO var tokenRequest dto.TokenRequestDTO
if err := c.ShouldBindJSON(&tokenRequest); err != nil { if err := c.ShouldBindJSON(&tokenRequest); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return return
} }
deleteSessionTokenCookie(c)
F.Pipe2( F.Pipe2(
s.authRepository.RevokeToken(c.Request.Context())(tokenRequest.Token), s.authRepository.RevokeToken(c.Request.Context())(tokenRequest.Token),
E.Map[error](func(userID string) gin.H { E.Map[error](func(userID string) gin.H {
@ -116,3 +109,26 @@ func (s *AuthService) Revoke(c *gin.Context) {
), ),
) )
} }
func setSessionTokenCookie(c *gin.Context, user entities.User) func(token string) string {
return func(token string) string {
c.SetCookie("session_token", token, 3600, "/", "localhost", false, true)
c.SetCookie("user_id", user.ID, 3600, "/", "localhost", false, true)
return token
}
}
func deleteSessionTokenCookie(c *gin.Context) {
c.SetCookie("session_token", "", -1, "/", "localhost", false, true)
c.SetCookie("user_id", "", -1, "/", "localhost", false, true)
}
func validatePassword(password string) func(user entities.User) E.Either[error, entities.User] {
return func(user entities.User) E.Either[error, entities.User] {
hashedPassword := data.GenerateSecureHash(password)
if user.Password != hashedPassword {
return E.Left[entities.User, error](app_error.NewAuthError("Invalid password"))
}
return E.Right[error](user)
}
}

View File

@ -0,0 +1,15 @@
package mappers
import (
"actatempus_backend/internal/application/services/dto"
"actatempus_backend/internal/domain/entities"
)
func ToTokenDTO(user entities.User) func(string) dto.TokenResponseDTO {
return func(token string) dto.TokenResponseDTO {
return dto.TokenResponseDTO{
Token: token,
UserID: user.ID,
}
}
}