package services import ( "errors" "fmt" configs "goGin/config" "time" "github.com/golang-jwt/jwt/v5" ) // TokenTypeAccess sabiti, auth middleware'in beklediği token türünü temsil eder. const TokenTypeAccess = "access" // JWTClaim, authorization middleware'inin beklediği claim yapısını temsil eder. // İleride ihtiyaç oldukça alanlar genişletilebilir. type JWTClaim struct { TokenType string IsAdmin bool UserID any } // JWTService, JWT ile ilgili operasyonları kapsayan servis. type JWTService struct { secret []byte } // NewJWTService yeni bir JWTService örneği döner. // Secret, config içindeki JWT_SECRET üzerinden okunur. func NewJWTService() *JWTService { secret := "" if configs.AppConfig != nil { secret = configs.AppConfig.JWTSecret } return &JWTService{ secret: []byte(secret), } } // ValidateToken verilen JWT'yi doğrular ve claim'leri döner. // HMAC (HS256 vb.) ile imzalanmış token'lar beklenir. func (s *JWTService) ValidateToken(tokenString string) (*JWTClaim, error) { if len(s.secret) == 0 { return nil, errors.New("jwt secret is not configured") } token, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) { // Sadece HMAC algoritmalarına izin veriyoruz. if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"]) } return s.secret, nil }) if err != nil { return nil, err } if !token.Valid { return nil, errors.New("invalid token") } claims, ok := token.Claims.(jwt.MapClaims) if !ok { return nil, errors.New("invalid token claims") } // Token tipi (access / refresh vs.) var tokenType string if v, ok := claims["token_type"].(string); ok { tokenType = v } else if v, ok := claims["tokenType"].(string); ok { tokenType = v } // Yönetici flag'i var isAdmin bool if v, ok := claims["is_admin"]; ok { switch vv := v.(type) { case bool: isAdmin = vv case float64: isAdmin = vv != 0 } } else if v, ok := claims["isAdmin"]; ok { if vv, ok2 := v.(bool); ok2 { isAdmin = vv } } // Kullanıcı ID'si (sub claim'i üzerinden) var userID any if v, ok := claims["sub"]; ok { userID = v } else if v, ok := claims["user_id"]; ok { userID = v } return &JWTClaim{ TokenType: tokenType, IsAdmin: isAdmin, UserID: userID, }, nil } // GenerateToken creates a short-lived access token func (s *JWTService) GenerateToken(userID uint, isAdmin bool) (string, error) { claims := jwt.MapClaims{ "sub": userID, "is_admin": isAdmin, "token_type": TokenTypeAccess, "exp": jwt.NewNumericDate(time.Now().Add(time.Duration(configs.AppConfig.AccessTokenExpireMinutes) * time.Minute)), "iat": jwt.NewNumericDate(time.Now()), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(s.secret) } // GenerateRefreshToken creates a long-lived refresh token func (s *JWTService) GenerateRefreshToken(userID uint) (string, error) { claims := jwt.MapClaims{ "sub": userID, "token_type": "refresh", "exp": jwt.NewNumericDate(time.Now().Add(time.Duration(configs.AppConfig.RefreshTokenExpireDays) * 24 * time.Hour)), "iat": jwt.NewNumericDate(time.Now()), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(s.secret) }