package auth import ( "fmt" "os" "strings" "time" "gitea.d3m0k1d.ru/d3m0k1d/d3m0k1d.ru/backend/internal/storage" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" ) var jwtSecret = []byte(os.Getenv("JWT_SECRET")) func GenerateJWT(user storage.User) (string, error) { token := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.MapClaims{ "id": user.ID, "email": user.Email, "login": user.GithubLogin, "github_id": user.GithubID, "avatar_url": user.AvatarURL, "exp": time.Now().Add(30 * 24 * time.Hour).Unix(), // 30 дней "iat": time.Now().Unix(), }) tokenString, err := token.SignedString(jwtSecret) if err != nil { return "", err } return tokenString, nil } func JWTMiddleware() gin.HandlerFunc { return func(c *gin.Context) { auth := c.GetHeader("Authorization") if !strings.HasPrefix(auth, "Bearer ") { c.AbortWithStatusJSON(401, gin.H{"error": "Bearer required"}) return } tokenString := strings.TrimPrefix(auth, "Bearer ") token, err := jwt.Parse(tokenString, 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 jwtSecret, nil }) if err != nil || !token.Valid { c.AbortWithStatusJSON(401, gin.H{"error": "invalid token"}) return } claims, ok := token.Claims.(jwt.MapClaims) if !ok { c.AbortWithStatusJSON(401, gin.H{"error": "invalid claims"}) return } idValue, idExists := claims["id"] if !idExists { c.AbortWithStatusJSON(401, gin.H{"error": "missing id in token"}) return } idFloat, ok := idValue.(float64) if !ok { c.AbortWithStatusJSON(401, gin.H{"error": "invalid id type in token"}) return } githubIDValue, githubExists := claims["github_id"] if !githubExists { c.AbortWithStatusJSON(401, gin.H{"error": "missing github_id in token"}) return } githubIDFloat, ok := githubIDValue.(float64) if !ok { c.AbortWithStatusJSON(401, gin.H{"error": "invalid github_id type in token"}) return } loginValue, loginExists := claims["login"] if !loginExists { c.AbortWithStatusJSON(401, gin.H{"error": "missing login in token"}) return } login, ok := loginValue.(string) if !ok { c.AbortWithStatusJSON(401, gin.H{"error": "invalid login type in token"}) return } c.Set("user_id", int(idFloat)) c.Set("github_id", int(githubIDFloat)) c.Set("login", login) c.Next() } } func RequireAdmin() gin.HandlerFunc { return func(c *gin.Context) { githubID, exists := c.Get("github_id") if !exists { c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"}) return } id := githubID.(int) if id != 173489813 { c.AbortWithStatusJSON(403, gin.H{"error": "access denied"}) return } c.Next() } } func ValidateJWT(tokenString string) (*storage.User, error) { token, err := jwt.Parse(tokenString, 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 jwtSecret, nil }) if err != nil || !token.Valid { return nil, err } claims, ok := token.Claims.(jwt.MapClaims) if !ok { return nil, fmt.Errorf("invalid claims") } if exp, ok := claims["exp"].(float64); ok { if time.Now().Unix() > int64(exp) { return nil, fmt.Errorf("token expired") } } idFloat, ok := claims["id"].(float64) if !ok { return nil, fmt.Errorf("invalid id in token") } githubIDFloat, ok := claims["github_id"].(float64) if !ok { return nil, fmt.Errorf("invalid github_id in token") } login, ok := claims["login"].(string) if !ok { return nil, fmt.Errorf("invalid login in token") } email, ok := claims["email"].(string) if !ok { return nil, fmt.Errorf("invalid email in token") } avatarURL, _ := claims["avatar_url"].(string) user := &storage.User{ ID: int(idFloat), GithubID: int(githubIDFloat), GithubLogin: login, Email: email, AvatarURL: avatarURL, } return user, nil }