todo/internal/auth/service/service.go
Michael Thomson e55d419d44
All checks were successful
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
auth services, middleware, and other stuff
2025-05-22 13:55:43 -04:00

91 lines
2.2 KiB
Go

package service
import (
"context"
"errors"
"log/slog"
"time"
"gitea.michaelthomson.dev/mthomson/habits/internal/auth"
userrepository "gitea.michaelthomson.dev/mthomson/habits/internal/user/repository"
"github.com/golang-jwt/jwt/v5"
)
var (
ErrNotFound error = errors.New("user cannot be found")
ErrUnauthorized error = errors.New("user password incorrect")
)
type UserRepository interface {
Create(ctx context.Context, user userrepository.UserRow) (userrepository.UserRow, error)
GetByEmail(ctx context.Context, email string) (userrepository.UserRow, error)
}
type AuthService struct {
logger *slog.Logger
jwtKey []byte
userRepository UserRepository
argon2IdHash *auth.Argon2IdHash
}
func NewAuthService(logger *slog.Logger, jwtKey []byte, userRepository UserRepository, argon2IdHash *auth.Argon2IdHash) *AuthService {
return &AuthService{
logger: logger,
jwtKey: jwtKey,
userRepository: userRepository,
argon2IdHash: argon2IdHash,
}
}
func (a AuthService) Login(ctx context.Context, email string, password string) (string, error) {
// get user if exists
userRow, err := a.userRepository.GetByEmail(ctx, email)
if err != nil {
if err == userrepository.ErrNotFound {
return "", ErrNotFound
}
a.logger.ErrorContext(ctx, err.Error())
return "", err
}
// compare hashed passswords
err = a.argon2IdHash.Compare(userRow.HashedPassword, userRow.Salt, []byte(password))
if err == auth.ErrNoMatch {
return "", ErrUnauthorized
}
if err != nil {
a.logger.ErrorContext(ctx, err.Error())
return "", err
}
// create token and return it
token, err := a.CreateToken(ctx, email)
if err != nil {
a.logger.ErrorContext(ctx, err.Error())
return "", err
}
return token, nil
}
func (a AuthService) CreateToken(ctx context.Context, email string) (string, error) {
// Create a new JWT token with claims
claims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": email,
"iss": "todo-app",
"aud": "user",
"exp": time.Now().Add(time.Hour).Unix(),
"iat": time.Now().Unix(),
})
tokenString, err := claims.SignedString(a.jwtKey)
if err != nil {
a.logger.ErrorContext(ctx, err.Error())
}
return tokenString, nil
}