madipo2611 a5a90bed7e v0.0.1
2025-04-28 15:14:02 +03:00

134 lines
3.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package auth
import (
"errors"
"fmt"
"tailly_back_v2/internal/domain"
"time"
"github.com/golang-jwt/jwt/v5"
"golang.org/x/crypto/bcrypt"
)
var (
ErrInvalidToken = errors.New("invalid token")
)
type TokenAuth struct {
accessTokenSecret string
refreshTokenSecret string
accessTokenExpiry time.Duration
refreshTokenExpiry time.Duration
}
func NewTokenAuth(accessSecret, refreshSecret string, accessExpiry, refreshExpiry time.Duration) *TokenAuth {
return &TokenAuth{
accessTokenSecret: accessSecret,
refreshTokenSecret: refreshSecret,
accessTokenExpiry: accessExpiry,
refreshTokenExpiry: refreshExpiry,
}
}
// GenerateTokens создает пару access и refresh токенов
func (a *TokenAuth) GenerateTokens(userID int) (*domain.Tokens, error) {
accessToken, err := a.generateAccessToken(userID)
if err != nil {
return nil, err
}
refreshToken, err := a.generateRefreshToken(userID)
if err != nil {
return nil, err
}
return &domain.Tokens{
AccessToken: accessToken,
RefreshToken: refreshToken,
}, nil
}
// generateAccessToken создает access токен (короткоживущий)
func (a *TokenAuth) generateAccessToken(userID int) (string, error) {
claims := jwt.RegisteredClaims{
Subject: fmt.Sprintf("%d", userID),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(a.accessTokenExpiry)),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(a.accessTokenSecret))
}
// generateRefreshToken создает refresh токен (долгоживущий)
func (a *TokenAuth) generateRefreshToken(userID int) (string, error) {
claims := jwt.RegisteredClaims{
Subject: fmt.Sprintf("%d", userID),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(a.refreshTokenExpiry)),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(a.refreshTokenSecret))
}
// ValidateAccessToken проверяет access токен и возвращает userID
func (a *TokenAuth) ValidateAccessToken(tokenString string) (int, error) {
token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(a.accessTokenSecret), nil
})
if err != nil {
return 0, err
}
if claims, ok := token.Claims.(*jwt.RegisteredClaims); ok && token.Valid {
var userID int
_, err := fmt.Sscanf(claims.Subject, "%d", &userID)
if err != nil {
return 0, ErrInvalidToken
}
return userID, nil
}
return 0, ErrInvalidToken
}
// ValidateRefreshToken проверяет refresh токен и возвращает userID
func (a *TokenAuth) ValidateRefreshToken(tokenString string) (int, error) {
token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(a.refreshTokenSecret), nil
})
if err != nil {
return 0, err
}
if claims, ok := token.Claims.(*jwt.RegisteredClaims); ok && token.Valid {
var userID int
_, err := fmt.Sscanf(claims.Subject, "%d", &userID)
if err != nil {
return 0, ErrInvalidToken
}
return userID, nil
}
return 0, ErrInvalidToken
}
// HashPassword хеширует пароль
func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(bytes), err
}
// CheckPasswordHash проверяет пароль с хешем
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}