Add JWT authentication

- Create register and login use cases
- Implement JWT service for token generation and validation
- Add authentication middleware to protect habit endpoints
- Create auth HTTP handlers (register, login)
- Update habit handlers to extract userID from JWT token
- Register/login endpoints: POST /auth/register, POST /auth/login
- Habit endpoints now require Bearer token in Authorization header
- Tested: register -> create habit -> list habits works correctly
This commit is contained in:
2025-11-26 01:05:08 +01:00
parent 3e8883d878
commit b2894fca70
8 changed files with 375 additions and 4 deletions
@@ -0,0 +1,58 @@
package commands
import (
"context"
"habit-tracker-api/internal/domain/entities"
"habit-tracker-api/internal/domain/repositories"
"habit-tracker-api/internal/shared/errors"
"golang.org/x/crypto/bcrypt"
)
type RegisterUserCommand struct {
Email string
Password string
Timezone string
}
type RegisterUserHandler struct {
userRepo repositories.UserRepository
}
func NewRegisterUserHandler(userRepo repositories.UserRepository) *RegisterUserHandler {
return &RegisterUserHandler{userRepo: userRepo}
}
func (h *RegisterUserHandler) Handle(ctx context.Context, cmd RegisterUserCommand) (string, error) {
if cmd.Email == "" || cmd.Password == "" {
return "", errors.ErrInvalidInput
}
if len(cmd.Password) < 8 {
return "", errors.ErrInvalidInput
}
existing, _ := h.userRepo.FindByEmail(ctx, cmd.Email)
if existing != nil {
return "", errors.ErrAlreadyExists
}
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(cmd.Password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
timezone := cmd.Timezone
if timezone == "" {
timezone = "UTC"
}
user := entities.NewUser(cmd.Email, string(hashedPassword), timezone)
if err := h.userRepo.Create(ctx, user); err != nil {
return "", err
}
return user.ID, nil
}