Files
Datarush/internal/auth/service/auth.go
T
2025-12-15 22:28:01 +03:00

165 lines
3.5 KiB
Go

package service
import (
"context"
"time"
"datarush/internal/auth/domain"
"github.com/golang-jwt/jwt/v5"
"golang.org/x/crypto/bcrypt"
)
const (
tokenExpiration = 24 * time.Hour
bcryptCost = 10
)
type UserRepository interface {
Create(ctx context.Context, user *domain.User) error
GetByEmail(ctx context.Context, email string) (*domain.User, error)
GetByID(ctx context.Context, id domain.ID) (*domain.User, error)
Update(ctx context.Context, user *domain.User) error
Delete(ctx context.Context, id domain.ID) error
}
type AuthService struct {
repo UserRepository
jwtSecret string
}
func NewAuthService(repo UserRepository, jwtSecret string) *AuthService {
return &AuthService{
repo: repo,
jwtSecret: jwtSecret,
}
}
func (s *AuthService) SignUp(ctx context.Context, email, username, password string) (string, error) {
// Check if user already exists
_, err := s.repo.GetByEmail(ctx, email)
if err == nil {
return "", domain.ErrUserAlreadyExists
}
if err != domain.ErrUserNotFound {
return "", err
}
// Hash password
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
if err != nil {
return "", err
}
// Create user
user := &domain.User{
ID: domain.NewID(),
Email: email,
Username: username,
Password: string(hashedPassword),
}
if err := s.repo.Create(ctx, user); err != nil {
return "", err
}
// Generate token
token, err := s.generateToken(user)
if err != nil {
return "", err
}
return token, nil
}
func (s *AuthService) SignIn(ctx context.Context, email, password string) (string, error) {
// Get user by email
user, err := s.repo.GetByEmail(ctx, email)
if err != nil {
if err == domain.ErrUserNotFound {
return "", domain.ErrInvalidPassword
}
return "", err
}
// Check password
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
return "", domain.ErrInvalidPassword
}
// Generate token
token, err := s.generateToken(user)
if err != nil {
return "", err
}
return token, nil
}
func (s *AuthService) ValidateToken(ctx context.Context, tokenString string) (*domain.UserWithoutPassword, error) {
userID, err := s.parseToken(tokenString)
if err != nil {
return nil, err
}
user, err := s.repo.GetByID(ctx, userID)
if err != nil {
return nil, err
}
return &domain.UserWithoutPassword{
ID: user.ID,
Email: user.Email,
Username: user.Username,
CreatedAt: user.CreatedAt,
}, nil
}
func (s *AuthService) generateToken(user *domain.User) (string, error) {
claims := jwt.MapClaims{
"user_id": user.ID.String(),
"email": user.Email,
"username": user.Username,
"exp": time.Now().Add(tokenExpiration).Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString([]byte(s.jwtSecret))
if err != nil {
return "", err
}
return tokenString, nil
}
func (s *AuthService) parseToken(tokenString string) (domain.ID, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte(s.jwtSecret), nil
})
if err != nil {
return domain.ID{}, domain.ErrInvalidToken
}
if !token.Valid {
return domain.ID{}, domain.ErrInvalidToken
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return domain.ID{}, domain.ErrInvalidToken
}
userIDStr, ok := claims["user_id"].(string)
if !ok {
return domain.ID{}, domain.ErrInvalidToken
}
userID, err := domain.ParseID(userIDStr)
if err != nil {
return domain.ID{}, domain.ErrInvalidToken
}
return userID, nil
}