128 lines
3.3 KiB
Go
128 lines
3.3 KiB
Go
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)
|
||
}
|