middleware

package
v0.0.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 18, 2026 License: MIT Imports: 15 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
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

func ClaimsFromContext(ctx context.Context) (*token.Claims, bool)

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 FiberAuth

func FiberAuth() fiber.Handler

FiberAuth es el middleware de autenticación nativo para Fiber

func FiberLogger

func FiberLogger() fiber.Handler

FiberLogger registra peticiones usando el Logger Global de GoKit

func FiberRecovery

func FiberRecovery() fiber.Handler

FiberRecovery captura panics en Fiber

func GinAuth

func GinAuth() gin.HandlerFunc

GinAuth es el middleware de autenticación nativo para Gin

func GinLogger

func GinLogger() gin.HandlerFunc

GinLogger registra peticiones usando el Logger Global de GoKit

func GinRecovery

func GinRecovery() gin.HandlerFunc

GinRecovery captura panics en los handlers de Gin

func RegisterFiberRoutes

func RegisterFiberRoutes(group fiber.Router, routes []Route[fiber.Handler], opts ...RegisterOption)

RegisterFiberRoutes registra un conjunto de rutas en un fiber.Router y deja constancia de cada endpoint en el logger global de GoKit.

Si se pasa WithAuthManager, las rutas con Protected: true reciben automáticamente el middleware de autenticación (validado contra ese manager) antes de llegar al handler. Sin WithAuthManager, Protected solo aparece en el log.

Ejemplo:

authGroup := r.Group("/auth")
middleware.RegisterFiberRoutes(authGroup, []middleware.Route[fiber.Handler]{
    {Method: "POST", Path: "/signup", Handler: handler.SignUp, Protected: false},
    {Method: "POST", Path: "/signin", Handler: handler.SignIn, Protected: false},
}, middleware.WithGroupName("auth"))

func RegisterGinRoutes

func RegisterGinRoutes(group *gin.RouterGroup, routes []Route[gin.HandlerFunc], opts ...RegisterOption)

RegisterGinRoutes registra un conjunto de rutas en un *gin.RouterGroup y deja constancia de cada endpoint en el logger global de GoKit.

Si se pasa WithAuthManager, las rutas con Protected: true reciben automáticamente el middleware de autenticación (validado contra ese manager) antes de llegar al handler. Sin WithAuthManager, Protected solo aparece en el log.

Ejemplo:

authGroup := r.Group("/auth")
middleware.RegisterGinRoutes(authGroup, []middleware.Route[gin.HandlerFunc]{
    {Method: "POST", Path: "/signup", Handler: handler.SignUp, Protected: false},
    {Method: "POST", Path: "/signin", Handler: handler.SignIn, Protected: false},
}, middleware.WithGroupName("auth"))

RegisterGinRoutes

func RemoteIPKeyFunc

func RemoteIPKeyFunc(r *http.Request) string

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.

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

type KeyFunc func(r *http.Request) string

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

type Middleware func(http.Handler) http.Handler

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)

RegisterOption modifica un RegisterOptions.

func WithAuthManager

func WithAuthManager(manager *token.JWTManager) RegisterOption

WithAuthManager habilita la aplicación automática de autenticación en las rutas marcadas como Protected: true, validando contra el manager dado.

func WithGroupName

func WithGroupName(name string) RegisterOption

WithGroupName etiqueta el grupo de rutas en el log de arranque.

type RegisterOptions

type RegisterOptions struct {
	// AuthManager, si se proporciona, hace que las rutas con
	// Protected: true reciban automáticamente un middleware de
	// autenticación validado contra este manager, además de reflejarlo en
	// el log. Si se omite, Protected es solo informativo.
	AuthManager *token.JWTManager

	// GroupName es una etiqueta opcional (ej. "auth", "admin") que se
	// incluye en el log de arranque para identificar a qué grupo
	// pertenece cada ruta registrada.
	GroupName string
}

RegisterOptions configura el comportamiento de RegisterGinRoutes y RegisterFiberRoutes.

type Route

type Route[H any] struct {
	// Method es el verbo HTTP en mayúsculas ("GET", "POST", "PUT", "DELETE", ...).
	Method string
	// Path es la ruta relativa al grupo/router en el que se registra.
	Path string
	// Handler es el handler nativo del framework para este endpoint.
	Handler H
	// Protected indica si el endpoint requiere autenticación. Por sí solo
	// solo afecta al log de arranque; para que además se aplique el
	// middleware de autenticación automáticamente, registra las rutas con
	// WithAuthManager.
	Protected bool
}

Route describe un endpoint a registrar en un router concreto. H es el tipo de handler nativo del framework (gin.HandlerFunc o fiber.Handler), lo que permite reutilizar la misma estructura tanto para Gin como para Fiber sin duplicar la definición de rutas.

Ejemplo:

routes := []middleware.Route[gin.HandlerFunc]{
    {Method: "POST", Path: "/signup", Handler: handler.SignUp, Protected: false},
    {Method: "POST", Path: "/signin", Handler: handler.SignIn, Protected: false},
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL