package utils import ( "errors" "os" "strconv" "strings" "time" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" ) // JWTClaims represents the JWT claims structure type JWTClaims struct { UserID uint `json:"user_id"` Email string `json:"email"` Role string `json:"role"` jwt.RegisteredClaims } // GenerateJWT generates a new JWT token for the given user func GenerateJWT(userID uint, email, role string) (string, error) { // Respect configurable expiration via JWT_EXPIRATION_HOURS; default 24h expHours := 24 if v := os.Getenv("JWT_EXPIRATION_HOURS"); v != "" { if n, err := strconv.Atoi(v); err == nil && n > 0 && n <= 24*365 { expHours = n } } expirationTime := time.Now().Add(time.Duration(expHours) * time.Hour) claims := &JWTClaims{ UserID: userID, Email: email, Role: role, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(expirationTime), IssuedAt: jwt.NewNumericDate(time.Now()), NotBefore: jwt.NewNumericDate(time.Now()), Issuer: "fotbal-club", }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString([]byte(GetJWTSecret())) if err != nil { return "", err } return tokenString, nil } // ParseJWT parses and validates a JWT token func ParseJWT(tokenString string) (*JWTClaims, error) { token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) { return []byte(GetJWTSecret()), nil }) if err != nil { return nil, err } if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid { return claims, nil } return nil, errors.New("invalid token") } // GetJWTSecret returns the JWT secret from environment variable or a default value func GetJWTSecret() string { secret := os.Getenv("JWT_SECRET") if secret == "" { return "default-secret-key-change-in-production" } return secret } // GetUserFromContext gets the authenticated user from Gin context func GetUserFromContext(c *gin.Context) (*JWTClaims, error) { // Preferred: claims set by auth middleware if v, ok := c.Get("claims"); ok { if cl, ok2 := v.(*JWTClaims); ok2 { return cl, nil } } // Fallback: parse Authorization header (Bearer ) authHeader := c.GetHeader("Authorization") if authHeader == "" { return nil, errors.New("authorization header missing") } parts := strings.Split(authHeader, " ") if len(parts) != 2 || parts[0] != "Bearer" { return nil, errors.New("invalid authorization header format") } token := parts[1] cl, err := ParseJWT(token) if err != nil { return nil, err } return cl, nil }