Files
goGin/app/services/jwt_service.go
Beyhan Oğur 2a5b661443 first commit
2026-04-26 21:46:42 +03:00

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