Documentation
¶
Overview ¶
Package middleware provee middlewares HTTP reutilizables para el ecosistema GoKit: autenticación JWT, autorización por rol, verificación de sesión activa, limitación de tasa (rate limiting), CORS y logging de peticiones.
Todos los middlewares siguen la firma estándar de Go (Middleware = func(http.Handler) http.Handler), por lo que son compatibles con net/http y con routers populares como chi, gorilla/mux o Gin (vía adaptador).
Ejemplo combinando varios middlewares con Chain:
handler := middleware.Chain(mux,
middleware.RequestLogger(log),
middleware.CORS(corsConfig),
middleware.RateLimit(limiter, nil),
)
http.ListenAndServe(":8080", handler)
Para proteger una ruta específica con autenticación y roles:
authed := middleware.RequireAuth(jwtManager)
admin := middleware.RequireRole("admin")
mux.Handle("/admin", authed(admin(adminHandler)))
Index ¶
- Variables
- func Chain(h http.Handler, mws ...Middleware) http.Handler
- func ClaimsFromContext(ctx context.Context) (*token.Claims, bool)
- func FiberAuth() fiber.Handler
- func FiberLogger() fiber.Handler
- func FiberRecovery() fiber.Handler
- func GinAuth() gin.HandlerFunc
- func GinAuthWithManager(manager *token.JWTManager) gin.HandlerFunc
- func GinRateLimit(limiter RateLimiter, keyFunc KeyFunc) gin.HandlerFunc
- func RegisterFiberRoutes(group fiber.Router, routes []Route[fiber.Handler], opts ...RegisterOption)
- func RegisterGinRoutes(group *gin.RouterGroup, routes []Route[gin.HandlerFunc], ...)
- func RemoteIPKeyFunc(r *http.Request) string
- func UserIDKeyFunc(req *http.Request) string
- type CORSConfig
- type KeyFunc
- type Logger
- type MemoryRateLimiter
- type Middleware
- func CORS(cfg CORSConfig) Middleware
- func RateLimit(limiter RateLimiter, keyFunc KeyFunc) Middleware
- func RequestLogger(log Logger) Middleware
- func RequireActiveSession(sessions *token.SessionManager) Middleware
- func RequireAuth(manager *token.JWTManager) Middleware
- func RequireRole(roles ...string) Middleware
- type RateLimiter
- type RegisterOption
- type RegisterOptions
- type Route
Constants ¶
This section is empty.
Variables ¶
var ( // ErrMissingAuthHeader se retorna cuando la petición no incluye el // header Authorization. ErrMissingAuthHeader = errors.New("falta el header Authorization") // ErrInvalidAuthHeader se retorna cuando el header Authorization no // tiene el formato esperado "Bearer <token>". ErrInvalidAuthHeader = errors.New("el header Authorization debe tener el formato 'Bearer <token>'") )
Functions ¶
func Chain ¶
func Chain(h http.Handler, mws ...Middleware) http.Handler
Chain compone varios middlewares en un único http.Handler. Se aplican en el orden en que se pasan: el primero de la lista es el más externo (se ejecuta primero en la petición, último en la respuesta).
Ejemplo de uso:
handler := middleware.Chain(finalHandler,
middleware.RequestLogger(log), // se ejecuta primero
middleware.CORS(corsConfig),
middleware.RequireAuth(jwtManager), // se ejecuta justo antes del handler
)
func ClaimsFromContext ¶
ClaimsFromContext extrae los claims del JWT validado por RequireAuth desde el contexto de la petición. El segundo valor devuelto es false si RequireAuth no se ejecutó antes en la cadena de middlewares (por ejemplo, si se llama desde una ruta pública).
Ejemplo de uso dentro de un handler protegido por RequireAuth:
func perfilHandler(w http.ResponseWriter, r *http.Request) {
claims, ok := middleware.ClaimsFromContext(r.Context())
if !ok {
http.Error(w, "no autorizado", http.StatusUnauthorized)
return
}
fmt.Fprintf(w, "Hola, %s", claims.Username)
}
func FiberLogger ¶
FiberLogger registra peticiones usando el Logger Global de GoKit
func GinAuth ¶
func GinAuth() gin.HandlerFunc
GinAuth es el middleware de autenticación nativo para Gin. Valida el JWT y guarda los claims tanto en el mapa interno de Gin como en el context.Context estándar de GoKit.
func GinAuthWithManager ¶
func GinAuthWithManager(manager *token.JWTManager) gin.HandlerFunc
GinAuthWithManager permite usar una instancia específica de JWTManager.
func GinRateLimit ¶
func GinRateLimit(limiter RateLimiter, keyFunc KeyFunc) gin.HandlerFunc
func RegisterFiberRoutes ¶
func RegisterGinRoutes ¶
func RegisterGinRoutes(group *gin.RouterGroup, routes []Route[gin.HandlerFunc], opts ...RegisterOption)
func RemoteIPKeyFunc ¶
RemoteIPKeyFunc usa la IP remota de la conexión TCP como clave de rate limit. Es el KeyFunc por defecto si RateLimit recibe nil.
Advertencia: si tu servicio está detrás de un proxy o balanceador de carga, r.RemoteAddr será la IP del proxy, no la del cliente real, y todas las peticiones compartirán el mismo límite. En ese caso, provee tu propio KeyFunc que lea (y valide) X-Forwarded-For o X-Real-IP según la configuración de tu infraestructura — sin validación, un cliente podría falsificar ese header para evadir el límite o afectar a otros.
func UserIDKeyFunc ¶
Types ¶
type CORSConfig ¶
type CORSConfig struct {
// AllowedOrigins es la lista de orígenes permitidos (ej.
// "https://app.example.com"). Usa "*" para permitir cualquier origen —
// pero ten en cuenta que la especificación CORS prohíbe combinar "*"
// con AllowCredentials = true; en ese caso, "*" se ignora.
AllowedOrigins []string
// AllowedMethods es la lista de métodos HTTP permitidos en la petición
// real. Si está vacía, se usa un conjunto por defecto razonable
// (GET, POST, PUT, PATCH, DELETE, OPTIONS).
AllowedMethods []string
// AllowedHeaders es la lista de headers que el cliente puede enviar.
// Si está vacía, se usa un conjunto por defecto (Authorization, Content-Type).
AllowedHeaders []string
// AllowCredentials indica si se permite el envío de cookies/credenciales
// (Access-Control-Allow-Credentials). No es compatible con
// AllowedOrigins = ["*"] según la especificación CORS.
AllowCredentials bool
// MaxAge es cuánto tiempo, en segundos, el navegador puede cachear la
// respuesta a una petición preflight (OPTIONS). Si es 0, no se envía
// la cabecera y el navegador usa su valor por defecto.
MaxAge int
}
CORSConfig define la configuración del middleware CORS.
type KeyFunc ¶
KeyFunc extrae la clave de limitación de tasa a partir de la petición (ej. la IP remota, un userID ya autenticado, una API key del header).
type Logger ¶
type Logger interface {
Info(message string)
}
Logger es la interfaz mínima que necesita RequestLogger. El *logger.Logger de GoKit (paquete github.com/AndresGT/GoKit/logger) la implementa directamente, ya que su método Info tiene esta misma firma; cualquier otro logger compatible funciona igual. No se importa el paquete logger aquí a propósito, para no forzar esa dependencia en quien no lo use.
type MemoryRateLimiter ¶
type MemoryRateLimiter struct {
// contains filtered or unexported fields
}
MemoryRateLimiter implementa RateLimiter en memoria usando el algoritmo de ventana fija (fixed window): permite como máximo 'limit' peticiones por 'window' y por clave.
Adecuado para una sola instancia del servicio. En despliegues con varias instancias/réplicas, el límite se aplica de forma independiente en cada una (no es un límite global) — para eso necesitas un backend compartido (ej. Redis) implementando la interfaz RateLimiter.
func NewMemoryRateLimiter ¶
func NewMemoryRateLimiter(limit int, window time.Duration) *MemoryRateLimiter
NewMemoryRateLimiter crea un limitador de tasa en memoria: como máximo 'limit' peticiones por 'window' y por clave. Si los valores no son válidos, se usan valores por defecto seguros (1 petición / 1 minuto).
func (*MemoryRateLimiter) Allow ¶
func (l *MemoryRateLimiter) Allow(key string) bool
Allow implementa RateLimiter.
func (*MemoryRateLimiter) Cleanup ¶
func (l *MemoryRateLimiter) Cleanup()
Cleanup elimina las claves cuya ventana ya expiró. El mapa interno de MemoryRateLimiter crece con cada clave nueva vista (IP, usuario...) y nunca se reduce por sí solo; en un proceso de larga duración esto es un crecimiento de memoria no acotado. Llama a Cleanup periódicamente (por ejemplo, con un time.Ticker cada pocos minutos) para evitarlo.
Ejemplo de uso:
limiter := middleware.NewMemoryRateLimiter(100, time.Minute)
go func() {
ticker := time.NewTicker(5 * time.Minute)
for range ticker.C {
limiter.Cleanup()
}
}()
type Middleware ¶
Middleware es el tipo estándar de middleware HTTP usado en este paquete: una función que envuelve un http.Handler con otro http.Handler. Es compatible con la firma que usan la mayoría de routers de Go.
func CORS ¶
func CORS(cfg CORSConfig) Middleware
CORS aplica las cabeceras CORS según la configuración proporcionada y responde directamente (204 No Content) a las peticiones preflight (OPTIONS), sin llegar al siguiente handler.
Ejemplo de uso:
cfg := middleware.CORSConfig{
AllowedOrigins: []string{"https://app.example.com"},
AllowCredentials: true,
}
handler := middleware.CORS(cfg)(mux)
func RateLimit ¶
func RateLimit(limiter RateLimiter, keyFunc KeyFunc) Middleware
RateLimit limita el número de peticiones permitidas por clave (ver KeyFunc). Responde 429 Too Many Requests cuando se supera el límite.
Si keyFunc es nil, se usa RemoteIPKeyFunc.
Ejemplo de uso:
limiter := middleware.NewMemoryRateLimiter(60, time.Minute) // 60 req/min por IP
handler := middleware.RateLimit(limiter, nil)(mux)
// Limitar por usuario autenticado en vez de por IP:
byUser := func(r *http.Request) string {
if claims, ok := middleware.ClaimsFromContext(r.Context()); ok {
return claims.UserID
}
return middleware.RemoteIPKeyFunc(r)
}
handler := middleware.RequireAuth(jwtManager)(
middleware.RateLimit(limiter, byUser)(mux),
)
func RequestLogger ¶
func RequestLogger(log Logger) Middleware
RequestLogger registra cada petición HTTP (método, ruta, status y duración) usando el Logger proporcionado. Si el handler nunca llama explícitamente a WriteHeader, se asume el código 200 por defecto, tal como hace net/http.
Ejemplo de uso:
log := logger.New() handler := middleware.RequestLogger(log)(mux)
func RequireActiveSession ¶
func RequireActiveSession(sessions *token.SessionManager) Middleware
RequireActiveSession verifica, además de la firma y expiración del JWT, que la sesión asociada (claims.SessionID) siga activa según el SessionManager proporcionado. Responde 401 Unauthorized si la sesión fue revocada, expiró o no existe.
Esto cubre una limitación inherente de los JWT: un access token firmado correctamente sigue siendo válido hasta que expira por sí solo, aunque el usuario haya cerrado sesión, cambiado su contraseña o se le haya revocado el acceso. Encadenar este middleware permite invalidar el acceso de forma inmediata a costa de una consulta adicional al SessionStore en cada petición — una decisión de diseño consciente entre rendimiento y capacidad de revocación instantánea; úsalo en las rutas donde esa garantía importe.
Debe encadenarse DESPUÉS de RequireAuth.
Ejemplo de uso:
authed := middleware.RequireAuth(jwtManager)
activeSession := middleware.RequireActiveSession(sessionManager)
mux.Handle("/perfil", authed(activeSession(http.HandlerFunc(perfilHandler))))
func RequireAuth ¶
func RequireAuth(manager *token.JWTManager) Middleware
RequireAuth valida el access token Bearer del header Authorization usando el JWTManager proporcionado. Si el token es válido, inyecta sus claims en el contexto de la petición (recuperables con ClaimsFromContext) y continúa la cadena; en caso contrario responde 401 Unauthorized sin llegar al siguiente handler.
Rechaza explícitamente los refresh tokens: solo un token cuyo TokenType sea token.TokenTypeAccess se considera una credencial de acceso válida.
Ejemplo de uso:
authed := middleware.RequireAuth(jwtManager)
mux.Handle("/perfil", authed(http.HandlerFunc(perfilHandler)))
func RequireRole ¶
func RequireRole(roles ...string) Middleware
RequireRole restringe el acceso a los usuarios cuyo claim Role esté entre los roles permitidos. Responde 403 Forbidden si el rol no está permitido.
Debe encadenarse DESPUÉS de RequireAuth: depende de los claims que este último inyecta en el contexto. Si RequireAuth no se ejecutó antes, responde 401 Unauthorized.
Ejemplo de uso:
authed := middleware.RequireAuth(jwtManager)
adminOnly := middleware.RequireRole("admin", "superadmin")
mux.Handle("/admin", authed(adminOnly(http.HandlerFunc(adminHandler))))
type RateLimiter ¶
type RateLimiter interface {
// Allow indica si, en este momento, se permite una nueva petición para
// la clave indicada (ej. una IP, un userID, una API key).
Allow(key string) bool
}
RateLimiter define el contrato para backends de limitación de tasa. Permite implementar el algoritmo o backend que prefieras (memoria, Redis, etc.) sin modificar el middleware RateLimit que lo consume.
type RegisterOption ¶
type RegisterOption func(*RegisterOptions)
func WithAuthManager ¶
func WithAuthManager(manager *token.JWTManager) RegisterOption
func WithGroupName ¶
func WithGroupName(name string) RegisterOption
type RegisterOptions ¶
type RegisterOptions struct {
AuthManager *token.JWTManager
GroupName string
}