add auth
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user