package middleware import ( "context" "errors" "net/http" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" ) // ctxKey — приватный тип для ключа контекста type ctxKey struct{} // ginContextKey — уникальный ключ для хранения *gin.Context var ginContextKey = &ctxKey{} // GinContextToContext сохраняет *gin.Context в context.Context запроса func GinContextToContext(c *gin.Context) { ctx := context.WithValue(c.Request.Context(), ginContextKey, c) c.Request = c.Request.WithContext(ctx) } // GinContextFromContext извлекает *gin.Context из context.Context func GinContextFromContext(ctx context.Context) (*gin.Context, bool) { ginCtx, ok := ctx.Value(ginContextKey).(*gin.Context) return ginCtx, ok } func JWTAuthMiddleware(secret string) gin.HandlerFunc { return func(c *gin.Context) { // 1. Получаем access_token из cookie tokenStr, err := c.Cookie("access_token") if err != nil { abortWithJSON(c, http.StatusUnauthorized, "missing access_token cookie") return } // 2. Парсим токен с MapClaims token, err := jwt.Parse(tokenStr, func(t *jwt.Token) (interface{}, error) { if t.Method != jwt.SigningMethodHS256 { return nil, errors.New("unexpected signing method: " + t.Method.Alg()) } return []byte(secret), nil // ← конвертируем string → []byte }) if err != nil { abortWithJSON(c, http.StatusUnauthorized, "invalid token: "+err.Error()) return } // 3. Проверяем валидность if !token.Valid { abortWithJSON(c, http.StatusUnauthorized, "token is invalid") return } // 4. Извлекаем user_id из claims claims, ok := token.Claims.(jwt.MapClaims) if !ok { abortWithJSON(c, http.StatusUnauthorized, "invalid claims format") return } userID, ok := claims["user_id"].(string) if !ok || userID == "" { abortWithJSON(c, http.StatusUnauthorized, "user_id claim missing or invalid") return } // 5. Сохраняем в контексте c.Set("user_id", userID) // 6. Для oapi-codegen — кладём gin.Context в request context GinContextToContext(c) c.Next() } } // Вспомогательные функции (без изменений) func UserIDFromGin(c *gin.Context) (string, bool) { id, exists := c.Get("user_id") if !exists { return "", false } if s, ok := id.(string); ok { return s, true } return "", false } func UserIDFromContext(ctx context.Context) (string, error) { ginCtx, ok := GinContextFromContext(ctx) if !ok { return "", errors.New("gin context not found") } userID, ok := UserIDFromGin(ginCtx) if !ok { return "", errors.New("user_id not found in context") } return userID, nil } func abortWithJSON(c *gin.Context, code int, message string) { c.AbortWithStatusJSON(code, gin.H{ "error": "unauthorized", "message": message, }) }