package service import ( "context" "errors" "fmt" "time" "bugulma/backend/internal/domain" "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" "golang.org/x/crypto/bcrypt" ) type AuthService struct { userRepo domain.UserRepository jwtService *JWTService } func NewAuthService(userRepo domain.UserRepository, jwtSecret string) *AuthService { return &AuthService{ userRepo: userRepo, jwtService: NewJWTService(jwtSecret), } } type Claims struct { UserID string `json:"user_id"` Email string `json:"email"` Name string `json:"name"` Role string `json:"role"` jwt.RegisteredClaims } func (s *AuthService) Login(ctx context.Context, email, password string) (string, *domain.User, error) { user, err := s.userRepo.GetByEmail(ctx, email) if err != nil { return "", nil, errors.New("invalid credentials") } // Verify password if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil { return "", nil, errors.New("invalid credentials") } // For now, use empty orgID - this would be determined by user's primary organization // In a real implementation, this might involve checking user's organization memberships token, err := s.jwtService.GenerateToken(user, "") if err != nil { return "", nil, err } return token, user, nil } func (s *AuthService) ValidateToken(ctx context.Context, tokenString string) (*domain.User, error) { claims, err := s.jwtService.ValidateToken(tokenString) if err != nil { return nil, err } user, err := s.userRepo.GetByID(ctx, claims.UserID) if err != nil { return nil, err } return user, nil } // ValidateTokenWithClaims validates token and returns both user and claims func (s *AuthService) ValidateTokenWithClaims(ctx context.Context, tokenString string) (*domain.User, *JWTClaims, error) { claims, err := s.jwtService.ValidateToken(tokenString) if err != nil { return nil, nil, err } user, err := s.userRepo.GetByID(ctx, claims.UserID) if err != nil { return nil, nil, err } return user, claims, nil } // Register creates a new user account and returns a JWT token func (s *AuthService) Register(ctx context.Context, email, password, name string, role domain.UserRole) (string, *domain.User, error) { // Check if email already exists existing, err := s.userRepo.GetByEmail(ctx, email) if err == nil && existing != nil { return "", nil, errors.New("email already registered") } // Hash password hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return "", nil, fmt.Errorf("failed to hash password: %w", err) } // Create new user user := &domain.User{ ID: uuid.New().String(), Email: email, Name: name, Password: string(hashedPassword), Role: role, IsActive: true, CreatedAt: time.Now(), UpdatedAt: time.Now(), } // Set default permissions based on role (empty array for now, can be extended) permissionsJSON := "[]" user.Permissions = permissionsJSON // Save user to database if err := s.userRepo.Create(ctx, user); err != nil { return "", nil, fmt.Errorf("failed to create user: %w", err) } // Generate JWT token token, err := s.jwtService.GenerateToken(user, "") if err != nil { return "", nil, fmt.Errorf("failed to generate token: %w", err) } return token, user, nil }