package service import ( "authorization/internal" "authorization/internal/config" "crypto/sha1" "errors" "fmt" "time" "github.com/golang-jwt/jwt" ) const ( refresh = "refresh" access = "access" ) type tokenClaims struct { jwt.StandardClaims UserId int `json:"user_id"` UserRole string `json:"user_role"` TokenType string `json:"token_type"` } type AuthService struct { userService UserService tokenConfigs config.TokenConfig } func newAuthService(userService UserService) *AuthService { return &AuthService{userService: userService} } func (s *AuthService) CreateUser(user internal.User) (int, error) { user.Password = s.generatePasswordHash(user.Password) return s.userService.CreateUser(user) } func (s *AuthService) GenerateToken(username string, password string) (string, string, error) { user, err := s.userService.GetUser(username, s.generatePasswordHash(password)) if err != nil { return "", "", err } accessTokenClaims := s.generateClaims(user.Id, user.UserRole, access) refreshTokenClaims := s.generateClaims(user.Id, user.UserRole, refresh) accessToken, err := accessTokenClaims.SignedString([]byte(s.tokenConfigs.AccessToken.GetSecretBytes())) if err != nil { return "", "", err } refreshToken, err := refreshTokenClaims.SignedString([]byte(s.tokenConfigs.RefreshToken.GetSecretBytes())) if err != nil { return "", "", err } return accessToken, refreshToken, nil } func (s *AuthService) ChangeUserRole(username string, userrole string) (string, error) { user, err := s.userService.ChangeUserRole(username, userrole) if err != nil { return "", err } return user, nil } func (s *AuthService) generateClaims(userId int, userRole string, tokenType string) *jwt.Token { tokenTTL := s.tokenConfigs.RefreshToken.GetTTL() if tokenType == access { tokenTTL = s.tokenConfigs.AccessToken.GetTTL() } return jwt.NewWithClaims(jwt.SigningMethodHS256, &tokenClaims{ jwt.StandardClaims{ ExpiresAt: time.Now().Add(tokenTTL).Unix(), IssuedAt: time.Now().Unix(), }, userId, userRole, tokenType, }) } func (s *AuthService) generatePasswordHash(password string) string { hash := sha1.New() hash.Write([]byte(password)) return fmt.Sprintf("%x", hash) } func (s *AuthService) ParseToken(accessToken string) (string, error) { claims, err := s.parseTokenWithSecret(accessToken, s.tokenConfigs.AccessToken.GetSecretBytes()) if err != nil { return "", err } return claims.UserRole, nil } func (s *AuthService) RefreshToken(refreshToken string) (string, string, error) { // Парсим refresh token claims, err := s.parseTokenWithSecret(refreshToken, s.tokenConfigs.RefreshToken.GetSecretBytes()) if err != nil { return "", "", errors.New("invalid refresh token") } // Проверяем, что это именно refresh token if claims.TokenType != refresh { return "", "", errors.New("token is not a refresh token") } // Генерируем новую пару токенов newAccessTokenClaims := s.generateClaims(claims.UserId, claims.UserRole, access) newRefreshTokenClaims := s.generateClaims(claims.UserId, claims.UserRole, refresh) newAccessToken, err := newAccessTokenClaims.SignedString([]byte(s.tokenConfigs.AccessToken.GetSecretBytes())) if err != nil { return "", "", err } newRefreshToken, err := newRefreshTokenClaims.SignedString([]byte(s.tokenConfigs.RefreshToken.GetSecretBytes())) if err != nil { return "", "", err } return newAccessToken, newRefreshToken, nil } // parseTokenWithSecret - общий метод для парсинга токена с заданным секретным ключом func (s *AuthService) parseTokenWithSecret(tokenString string, secret []byte) (*tokenClaims, error) { token, err := jwt.ParseWithClaims(tokenString, &tokenClaims{}, func(t *jwt.Token) (interface{}, error) { return secret, nil }) if err != nil { return nil, err } claims, ok := token.Claims.(*tokenClaims) if !ok { return nil, errors.New("invalid token claims") } return claims, nil }