8b40d1bfe5
modified: internal/domain/auth/dto.go modified: internal/domain/auth/handler.go modified: internal/domain/auth/router.go modified: internal/domain/auth/servcie.go modified: internal/middleware/auth.go modified: internal/router/router.go auth implemented without reset password
143 lines
4.4 KiB
Go
143 lines
4.4 KiB
Go
// middleware/auth.go (обновленная версия с логированием)
|
||
package middleware
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"net/http"
|
||
"strings"
|
||
|
||
"api_yal/internal/logger"
|
||
|
||
"github.com/golang-jwt/jwt/v5"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
type contextKey string
|
||
|
||
const (
|
||
UserIDKey contextKey = "userID"
|
||
UserEmailKey contextKey = "userEmail"
|
||
UserRoleKey contextKey = "userRole"
|
||
)
|
||
|
||
// AuthMiddleware создает middleware для проверки JWT токена
|
||
func AuthMiddleware(jwtSecret string) func(http.Handler) http.Handler {
|
||
return func(next http.Handler) http.Handler {
|
||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
l := logger.Get()
|
||
|
||
l.Info("=== AUTH MIDDLEWARE START ===")
|
||
l.Info("Request path", zap.String("path", r.URL.Path))
|
||
|
||
// Получаем токен из заголовка Authorization
|
||
authHeader := r.Header.Get("Authorization")
|
||
l.Info("Authorization header", zap.String("header", authHeader))
|
||
|
||
if authHeader == "" {
|
||
l.Warn("Отсутствует заголовок Authorization")
|
||
http.Error(w, "Authorization header required", http.StatusUnauthorized)
|
||
return
|
||
}
|
||
|
||
// Ожидаем формат "Bearer <token>"
|
||
parts := strings.Split(authHeader, " ")
|
||
if len(parts) != 2 || strings.ToLower(parts[0]) != "bearer" {
|
||
l.Warn("Неверный формат заголовка Authorization",
|
||
zap.Int("parts_count", len(parts)),
|
||
zap.String("first_part", parts[0]))
|
||
http.Error(w, "Invalid authorization header format", http.StatusUnauthorized)
|
||
return
|
||
}
|
||
|
||
tokenString := parts[1]
|
||
l.Info("Token extracted", zap.String("token_preview", tokenString[:min(20, len(tokenString))]+"..."))
|
||
|
||
// Парсим и валидируем токен
|
||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||
// Проверяем метод подписи
|
||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||
l.Error("Unexpected signing method",
|
||
zap.String("method", token.Method.Alg()))
|
||
return nil, jwt.ErrSignatureInvalid
|
||
}
|
||
return []byte(jwtSecret), nil
|
||
})
|
||
|
||
if err != nil {
|
||
l.Error("Token parse error", zap.Error(err))
|
||
http.Error(w, "Invalid token: "+err.Error(), http.StatusUnauthorized)
|
||
return
|
||
}
|
||
|
||
if !token.Valid {
|
||
l.Error("Token is not valid")
|
||
http.Error(w, "Invalid token", http.StatusUnauthorized)
|
||
return
|
||
}
|
||
|
||
l.Info("Token is valid")
|
||
|
||
// Извлекаем claims
|
||
claims, ok := token.Claims.(jwt.MapClaims)
|
||
if !ok {
|
||
l.Error("Failed to extract claims")
|
||
http.Error(w, "Invalid token claims", http.StatusUnauthorized)
|
||
return
|
||
}
|
||
|
||
l.Info("Claims extracted", zap.Any("claims", claims))
|
||
|
||
// Проверяем тип токена (должен быть access)
|
||
if tokenType, exists := claims["type"]; exists {
|
||
l.Info("Token type", zap.String("type", tokenType.(string)))
|
||
if tokenType != "access" {
|
||
l.Error("Wrong token type, expected access", zap.String("type", tokenType.(string)))
|
||
http.Error(w, "Invalid token type", http.StatusUnauthorized)
|
||
return
|
||
}
|
||
}
|
||
|
||
// Добавляем информацию о пользователе в контекст
|
||
ctx := r.Context()
|
||
|
||
// Извлекаем userID из sub (subject)
|
||
// В claims sub хранится как string, а не float64
|
||
if userID, ok := claims["sub"].(string); ok {
|
||
l.Info("User ID from claims", zap.String("user_id_str", userID))
|
||
// Конвертируем string в uint
|
||
var userIDUint uint
|
||
if _, err := fmt.Sscan(userID, &userIDUint); err == nil {
|
||
ctx = context.WithValue(ctx, UserIDKey, userIDUint)
|
||
l.Info("User ID added to context", zap.Uint("user_id", userIDUint))
|
||
}
|
||
} else {
|
||
l.Error("sub claim not found or wrong type")
|
||
}
|
||
|
||
// Извлекаем email
|
||
if email, ok := claims["email"].(string); ok {
|
||
ctx = context.WithValue(ctx, UserEmailKey, email)
|
||
l.Info("Email added to context", zap.String("email", email))
|
||
}
|
||
|
||
// Извлекаем роль
|
||
if role, ok := claims["role"].(string); ok {
|
||
ctx = context.WithValue(ctx, UserRoleKey, role)
|
||
l.Info("Role added to context", zap.String("role", role))
|
||
}
|
||
|
||
l.Info("=== AUTH MIDDLEWARE END ===")
|
||
|
||
// Передаем управление дальше с обновленным контекстом
|
||
next.ServeHTTP(w, r.WithContext(ctx))
|
||
})
|
||
}
|
||
}
|
||
|
||
func min(a, b int) int {
|
||
if a < b {
|
||
return a
|
||
}
|
||
return b
|
||
} |