Skip to content

Repository files navigation

gomiddleware

Middleware package for Go + Gin.

Install

go get github.com/whitebyte0/gomiddleware

Security Headers

import"github.com/whitebyte0/gomiddleware/security"// Defaults (no HSTS)router.Use(security.SecurityHeaders())
// Production — force HTTPSrouter.Use(security.SecurityHeaders(security.SecurityHeadersConfig{
ForceHTTPS: true,
}))
// Custom override (use "-" to skip a header)router.Use(security.SecurityHeaders(security.SecurityHeadersConfig{
FrameOptions: "SAMEORIGIN",
ContentSecurity: "-", // skip CSPForceHTTPS: true,
}))

Default headers: X-Frame-Options: DENY, X-Content-Type-Options: nosniff, X-XSS-Protection, Referrer-Policy, Content-Security-Policy, Permissions-Policy.

Recovery

Catches panics, logs full request context (method, path, IP, user agent, body, stack trace), returns 500 JSON.

import"github.com/whitebyte0/gomiddleware/recovery"// Default — logs to stderrrouter.Use(recovery.Recovery(nil))
// Custom handlerrouter.Use(recovery.Recovery(func(c*gin.Context, errany, stackstring) {
myLogger.Error("panic", "error", err, "stack", stack)
}))

CORS

Three levels: dev preset, production preset, full custom.

import"github.com/whitebyte0/gomiddleware/cors"// Development — allows all originsrouter.Use(cors.CORSDev())
// Production — strict origin list, credentials, 24h preflight cacherouter.Use(cors.CORSProd("https://vektor-x.com"))
// Full control — UseDefaults fills empty fields with sensible defaultsrouter.Use(cors.CORSWithConfig(cors.CORSConfig{
AllowOrigins: []string{"https://example.com"},
AllowMethods: []string{"GET", "POST"},
UseDefaults: true,
}))

Rate Limiting

Token bucket algorithm via golang.org/x/time/rate. Per-key with automatic cleanup.

import"github.com/whitebyte0/gomiddleware/ratelimit"// By IP — 10 requests per minuterouter.Use(ratelimit.IPRateLimit(10, time.Minute))
// By authenticated user — 100 requests per hourrouter.Use(ratelimit.UserRateLimit(100, time.Hour))
// Custom key functionrouter.Use(ratelimit.RateLimit(1.0, 10, func(c*gin.Context) string {
returnc.GetHeader("X-API-Key")
}))
// Per-routeapi.POST("/auth/login", ratelimit.IPRateLimit(5, time.Minute), loginHandler)

Returns 429 Too Many Requests with Retry-After header when exceeded.

Password Hashing

Hasher interface with bcrypt and Argon2id implementations.

import"github.com/whitebyte0/gomiddleware/password"// Bcrypt (default cost 12)hasher:=password.NewBcryptHasher()
// Argon2id (recommended for new projects)hasher:=password.NewArgon2idHasher()
// Both use the same interfacehash, err:=hasher.Hash("password123")
ok, err:=hasher.Verify("password123", hash)

JWT

Generate, validate, refresh, and revoke JWT tokens.

import"github.com/whitebyte0/gomiddleware/jwt"m:=jwt.NewJWTManager(jwt.JWTConfig{
Secret: "your-secret-minimum-32-bytes-long!!",
Issuer: "my-app",
Audience: "my-app",
})
// Generatetoken, err:=m.Generate(jwt.GenerateInput{
UserID: 42, Email: "user@test.com", Role: "admin",
SessionToken: session.SessionToken,
})
// Validateclaims, err:=m.Validate(token)
// Refresh (only expired tokens within refresh window)newToken, err:=m.Refresh(expiredToken)
// Revokem.Revoke(token, userID, "logout")

Built-in GORM stores

// Token revocation storerevokedStore:=jwt.NewGORMRevokedTokenStore(db)
// Token version provider (reads token_version from users table)versionProvider:=jwt.NewGORMTokenVersionProvider(db)
m:=jwt.NewJWTManager(jwt.JWTConfig{
Secret: "...",
Issuer: "my-app",
Audience: "my-app",
RevokedTokenStore: revokedStore,
TokenVersionProvider: versionProvider,
})

Or implement jwt.RevokedTokenStore and jwt.TokenVersionProvider with your own storage.

Config defaults

FieldDefault
Expiration1 hour
RefreshWindow24 hours

Sessions

GORM-backed database sessions with sliding window expiration.

import"github.com/whitebyte0/gomiddleware/session"sm:=session.NewSessionManager(db, 24*time.Hour)
sess, err:=sm.Create(userID, ipAddress, userAgent) // loginsess, err:=sm.Validate(sessionToken) // checksm.UpdateActivity(sessionToken) // sliding windowsm.Delete(sessionToken) // single logoutsm.DeleteAll(userID) // logout everywheresm.DeleteByID(sessionID, userID) // revoke specificsessions, err:=sm.List(userID) // list activedeleted, err:=sm.CleanupExpired() // background cleanup

Auth Middleware

Extracts Bearer token, validates, sets user context.

import"github.com/whitebyte0/gomiddleware/auth"// Required auth — returns 401 if missing/invalidrouter.Use(auth.Auth(jwtManager))
// Optional auth — continues without context if no tokenrouter.Use(auth.OptionalAuth(jwtManager))
// Access context in handlersuserID, ok:=auth.GetUserID(c)
email, ok:=auth.GetEmail(c)
role, ok:=auth.GetRole(c)
sessionToken, ok:=auth.GetSessionToken(c)

jwt.JWTManager implements auth.TokenValidator — pass it directly.

Role-Based Access

import"github.com/whitebyte0/gomiddleware/auth"router.POST("/admin/users", auth.RequireRole("admin", "moderator"), handler)
router.DELETE("/admin/users/:id", auth.RequireAdmin(), handler)

Roles are strings — define your own.

Audit

Async request logging with public path logging and body capture.

import"github.com/whitebyte0/gomiddleware/audit"router.Use(audit.Audit(audit.AuditConfig{
Store: &myAuditStore{db: db},
SkipPaths: []string{"/health", "/ready"},
// Log even without auth (e.g. login attempts)PublicPaths: []string{"/api/auth/login", "/api/auth/register"},
// Capture request body on these paths (supports wildcards)LogBodyPaths: []string{"/api/auth/*", "/api/admin/*"},
MaxBodySize: 4096, // default 4KB
}))
// Tag routes with resource typesuserRoutes:=router.Group("/users")
userRoutes.Use(audit.ResourceType("user"))

Implement audit.AuditStore:

typeAuditStoreinterface {
Save(entryAuditEntry) error
}

Action is auto-detected: POST→created, PUT/PATCH→updated, DELETE→deleted, GET→accessed.

Path matching supports exact ("/api/auth/login") and wildcard ("/api/admin/*").

License

MIT

About

Golang middlewares bundle for web APIs

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages