Middleware
This guide covers the middleware system in Swit, which provides configurable request processing pipelines for HTTP and gRPC transports, including authentication, authorization, rate limiting, CORS, and security features.
Overview
The middleware layer (pkg/middleware) provides a comprehensive set of middleware components for both HTTP (Gin) and gRPC transports. It includes built-in support for security, observability, and traffic management.
HTTP Middleware
Authentication Middleware
OAuth2/OIDC Authentication
go
import (
"github.com/innovationmech/swit/pkg/security/oauth2"
"github.com/innovationmech/swit/pkg/middleware"
)
// Create OAuth2 client
oauth2Client, err := oauth2.NewClient(&oauth2.Config{
Provider: "keycloak",
ClientID: "my-service",
ClientSecret: os.Getenv("OAUTH2_CLIENT_SECRET"),
IssuerURL: "https://auth.example.com/realms/production",
UseDiscovery: true,
})
// Create authentication middleware
authMiddleware := middleware.NewOAuth2Middleware(oauth2Client)
// Apply to routes
router.Use(authMiddleware.Authenticate())
// Or apply to specific route groups
protected := router.Group("/api/v1")
protected.Use(authMiddleware.Authenticate())
{
protected.GET("/users", getUsers)
protected.POST("/users", createUser)
}JWT Authentication
go
import (
"github.com/innovationmech/swit/pkg/security/jwt"
"github.com/innovationmech/swit/pkg/middleware"
)
// Create JWT validator
validator, err := jwt.NewValidator(&jwt.ValidatorConfig{
JWKSConfig: &jwt.JWKSConfig{
URL: "https://auth.example.com/.well-known/jwks.json",
CacheEnabled: true,
CacheTTL: time.Hour,
},
Issuer: "https://auth.example.com",
Audience: "my-service-api",
})
// Create JWT middleware
jwtMiddleware := middleware.NewJWTMiddleware(validator)
// Apply to routes
router.Use(jwtMiddleware.Authenticate())Authorization Middleware
OPA Policy Authorization
go
import (
"github.com/innovationmech/swit/pkg/security/opa"
"github.com/innovationmech/swit/pkg/middleware"
)
// Create OPA client
opaClient, err := opa.NewClient(&opa.Config{
Mode: "embedded",
DefaultDecisionPath: "authz/allow",
EmbeddedConfig: &opa.EmbeddedConfig{
PolicyDir: "./policies",
},
})
// Create authorization middleware
authzMiddleware := middleware.NewOPAMiddleware(opaClient, middleware.OPAMiddlewareConfig{
InputBuilder: func(c *gin.Context) map[string]interface{} {
return map[string]interface{}{
"subject": map[string]interface{}{
"user": c.GetString("user_id"),
"roles": c.GetStringSlice("roles"),
},
"action": c.Request.Method,
"resource": c.Request.URL.Path,
}
},
OnDeny: func(c *gin.Context) {
c.JSON(403, gin.H{"error": "access denied"})
c.Abort()
},
})
// Apply after authentication
router.Use(authMiddleware.Authenticate())
router.Use(authzMiddleware.Authorize())Rate Limiting Middleware
go
import "github.com/innovationmech/swit/pkg/middleware"
// IP-based rate limiting
rateLimiter := middleware.NewRateLimiter(middleware.RateLimiterConfig{
RequestsPerSecond: 100,
Burst: 200,
KeyFunc: func(c *gin.Context) string {
return c.ClientIP()
},
})
router.Use(rateLimiter)
// User-based rate limiting
userRateLimiter := middleware.NewRateLimiter(middleware.RateLimiterConfig{
RequestsPerSecond: 1000,
Burst: 2000,
KeyFunc: func(c *gin.Context) string {
return "user:" + c.GetString("user_id")
},
})
// Apply after authentication
router.Use(authMiddleware.Authenticate())
router.Use(userRateLimiter)CORS Middleware
go
import "github.com/gin-contrib/cors"
corsConfig := cors.Config{
AllowOrigins: []string{
"https://app.example.com",
"https://admin.example.com",
},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}
router.Use(cors.New(corsConfig))Timeout Middleware
go
import "github.com/innovationmech/swit/pkg/middleware"
timeoutMiddleware := middleware.NewTimeoutMiddleware(middleware.TimeoutConfig{
RequestTimeout: 30 * time.Second,
HandlerTimeout: 25 * time.Second,
})
router.Use(timeoutMiddleware)Request Logging Middleware
go
import "github.com/innovationmech/swit/pkg/middleware"
loggingMiddleware := middleware.NewLoggingMiddleware(middleware.LoggingConfig{
SkipPaths: []string{"/health", "/metrics"},
LogHeaders: false,
LogBody: false,
})
router.Use(loggingMiddleware)Security Headers Middleware
go
import "github.com/innovationmech/swit/pkg/middleware"
securityHeaders := middleware.NewSecurityHeadersMiddleware(middleware.SecurityHeadersConfig{
ContentSecurityPolicy: "default-src 'self'",
XContentTypeOptions: "nosniff",
XFrameOptions: "DENY",
XSSProtection: "1; mode=block",
})
router.Use(securityHeaders)gRPC Interceptors
Authentication Interceptor
go
import (
"github.com/innovationmech/swit/pkg/middleware"
"google.golang.org/grpc"
)
// Create authentication interceptor
authInterceptor := middleware.NewGRPCAuthInterceptor(oauth2Client)
// Apply to gRPC server
server := grpc.NewServer(
grpc.UnaryInterceptor(authInterceptor.UnaryInterceptor()),
grpc.StreamInterceptor(authInterceptor.StreamInterceptor()),
)Authorization Interceptor
go
authzInterceptor := middleware.NewGRPCOPAInterceptor(opaClient)
server := grpc.NewServer(
grpc.ChainUnaryInterceptor(
authInterceptor.UnaryInterceptor(),
authzInterceptor.UnaryInterceptor(),
),
)Logging Interceptor
go
loggingInterceptor := middleware.NewGRPCLoggingInterceptor()
server := grpc.NewServer(
grpc.UnaryInterceptor(loggingInterceptor.UnaryInterceptor()),
)Recovery Interceptor
go
recoveryInterceptor := middleware.NewGRPCRecoveryInterceptor()
server := grpc.NewServer(
grpc.UnaryInterceptor(recoveryInterceptor.UnaryInterceptor()),
)Middleware Chain
HTTP Middleware Chain
go
// Recommended middleware order
router := gin.New()
// 1. Recovery (catch panics)
router.Use(gin.Recovery())
// 2. Request ID
router.Use(middleware.NewRequestIDMiddleware())
// 3. Logging
router.Use(middleware.NewLoggingMiddleware(loggingConfig))
// 4. Security headers
router.Use(middleware.NewSecurityHeadersMiddleware(securityConfig))
// 5. CORS
router.Use(cors.New(corsConfig))
// 6. Rate limiting
router.Use(middleware.NewRateLimiter(rateLimitConfig))
// 7. Timeout
router.Use(middleware.NewTimeoutMiddleware(timeoutConfig))
// 8. Authentication
router.Use(authMiddleware.Authenticate())
// 9. Authorization
router.Use(authzMiddleware.Authorize())gRPC Interceptor Chain
go
server := grpc.NewServer(
grpc.ChainUnaryInterceptor(
recoveryInterceptor.UnaryInterceptor(),
loggingInterceptor.UnaryInterceptor(),
rateLimitInterceptor.UnaryInterceptor(),
authInterceptor.UnaryInterceptor(),
authzInterceptor.UnaryInterceptor(),
),
grpc.ChainStreamInterceptor(
recoveryInterceptor.StreamInterceptor(),
loggingInterceptor.StreamInterceptor(),
authInterceptor.StreamInterceptor(),
authzInterceptor.StreamInterceptor(),
),
)Configuration
YAML Configuration
yaml
http:
middleware:
enable_cors: true
enable_auth: true
enable_rate_limit: true
enable_logging: true
enable_timeout: true
cors:
allow_origins:
- "https://app.example.com"
allow_methods:
- "GET"
- "POST"
- "PUT"
- "DELETE"
allow_headers:
- "Origin"
- "Content-Type"
- "Authorization"
allow_credentials: true
max_age: 86400
rate_limit:
requests_per_second: 100
burst_size: 200
key_func: "ip"
timeout:
request_timeout: 30s
handler_timeout: 25s
grpc:
interceptors:
enable_auth: true
enable_logging: true
enable_recovery: true
enable_rate_limit: falseCustom Middleware
Creating Custom HTTP Middleware
go
func CustomMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Before request
startTime := time.Now()
// Process request
c.Next()
// After request
duration := time.Since(startTime)
log.Printf("Request took %v", duration)
}
}
router.Use(CustomMiddleware())Creating Custom gRPC Interceptor
go
func CustomUnaryInterceptor() grpc.UnaryServerInterceptor {
return func(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (interface{}, error) {
// Before RPC
startTime := time.Now()
// Process RPC
resp, err := handler(ctx, req)
// After RPC
duration := time.Since(startTime)
log.Printf("RPC %s took %v", info.FullMethod, duration)
return resp, err
}
}Best Practices
- Order Matters: Apply middleware in the correct order (recovery → logging → auth → business logic)
- Use Timeout: Always set request timeouts to prevent resource exhaustion
- Rate Limit Early: Apply rate limiting before expensive operations
- Authenticate Before Authorize: Always authenticate before authorization checks
- Log Appropriately: Don't log sensitive data (passwords, tokens)
- Handle Errors Gracefully: Use recovery middleware to catch panics
- Configure CORS Properly: Don't use
*for origins in production