package service import ( "context" "errors" "time" "go-backend-template/internal/contract" "go-backend-template/internal/entities" "go-backend-template/internal/repository" "go-backend-template/internal/transformer" "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" "golang.org/x/crypto/bcrypt" ) type AuthServiceImpl struct { userRepo *repository.UserRepositoryImpl jwtSecret string } func NewAuthService(userRepo *repository.UserRepositoryImpl, jwtSecret string) *AuthServiceImpl { return &AuthServiceImpl{ userRepo: userRepo, jwtSecret: jwtSecret, } } func (s *AuthServiceImpl) Login(ctx context.Context, req *contract.LoginRequest) (*contract.LoginResponse, error) { user, err := s.userRepo.GetByEmail(ctx, req.Email) if err != nil { return nil, errors.New("invalid credentials") } if !user.IsActive { return nil, errors.New("user account is inactive") } if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)); err != nil { return nil, errors.New("invalid credentials") } token, err := s.generateToken(user) if err != nil { return nil, err } expiresAt := time.Now().Add(24 * time.Hour) return &contract.LoginResponse{ Token: token, ExpiresAt: expiresAt, User: transformer.EntityToContract(user), }, nil } func (s *AuthServiceImpl) RefreshToken(ctx context.Context, req *contract.RefreshTokenRequest) (*contract.LoginResponse, error) { claims, err := s.ValidateToken(req.RefreshToken) if err != nil { return nil, errors.New("invalid refresh token") } userID, err := uuid.Parse(claims.Subject) if err != nil { return nil, errors.New("invalid user ID in token") } user, err := s.userRepo.GetByID(ctx, userID) if err != nil { return nil, errors.New("user not found") } if !user.IsActive { return nil, errors.New("user account is inactive") } token, err := s.generateToken(user) if err != nil { return nil, err } expiresAt := time.Now().Add(24 * time.Hour) return &contract.LoginResponse{ Token: token, ExpiresAt: expiresAt, User: transformer.EntityToContract(user), }, nil } func (s *AuthServiceImpl) GetProfile(ctx context.Context, userID uuid.UUID) (*contract.UserResponse, error) { user, err := s.userRepo.GetByID(ctx, userID) if err != nil { return nil, err } return transformer.EntityToContract(user), nil } func (s *AuthServiceImpl) generateToken(user *entities.User) (string, error) { claims := jwt.RegisteredClaims{ Subject: user.ID.String(), ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), IssuedAt: jwt.NewNumericDate(time.Now()), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(s.jwtSecret)) } func (s *AuthServiceImpl) ValidateToken(tokenString string) (*jwt.RegisteredClaims, error) { token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) { return []byte(s.jwtSecret), nil }) if err != nil { return nil, err } if claims, ok := token.Claims.(*jwt.RegisteredClaims); ok && token.Valid { return claims, nil } return nil, errors.New("invalid token") } func (s *AuthServiceImpl) GetUserByID(ctx context.Context, userID uuid.UUID) (*entities.User, error) { return s.userRepo.GetByID(ctx, userID) }