Middleware
Middleware runs before your controller handlers. For who is logged in and what they may do, EZ-Console layers authentication and RBAC on the API router; this page is the canonical place for route registration patterns. Conceptual background (JWT, OAuth, LDAP, MFA) lives in Authentication & Authorization.
Default API authenticationβ
When controllers register routes on the stock API group, the framework already applies WithAuthentication: every route expects a valid login (for example a bearer token) unless you explicitly opt out.
You do not need to call middleware.RequireAuth() on each handler for that default behaviorβit is already part of the groupβs middleware chain.
Public routes (WithoutAuthentication)β
Because WithAuthentication is registered on the API group by default, it runs before most handlers. For login-free endpoints you must wrap the route group with middleware.WithoutAuthentication, which inserts WithoutAuthenticationMiddleware ahead of the default authentication middleware so those routes are evaluated first.
site := middleware.WithoutAuthentication(router.Group("/site"))
site.GET("", c.GetSite)
Use this only for endpoints that are intentionally public (health, site metadata, OAuth callbacks, etc.).
Permission checks (RequirePermission)β
After authentication, some routes should be callable by any logged-in user, while others need specific RBAC permissions. Add middleware.RequirePermission (or the variants below) only where you need authorization beyond βlogged inβ.
chat := router.Group("/chat")
chat.GET("/sessions", c.ListChatSessions) // any authenticated user
chat.POST("/sessions", middleware.RequirePermission("ai:chat:create"), c.CreateChatSession) // requires ai:chat:create
Additional helpers (same package):
middleware.RequireAnyPermission(...)β grant if the user has any of the listed permissionsmiddleware.RequireAllPermissions(...)β grant only if the user has all of them
Permission naming and RBAC model: Authentication & Authorization.
Other common middlewareβ
Examples below assume github.com/sven-victor/ez-console/pkg/middleware (names may vary slightly by version; prefer your moduleβs API).
CORSβ
router.Use(middleware.CORS())
Loggingβ
router.Use(middleware.Logging())
Recovery (panic handler)β
router.Use(middleware.Recovery())
Global middleware (WithEngineOptions)β
For a normal EZ-Console binary you do not construct the Gin *gin.Engine in main: the framework creates it when the root command runs (consoleserver.NewCommandServer β internal server startup).
To run middleware on all routes, register one or more func(*gin.Engine) hooks with consoleserver.WithEngineOptions when calling NewCommandServer. Each hook receives the engine after the stock middleware (recovery, OpenTelemetry, Prometheus, request logging, CORS, delay) and before services and API routes are wired.
var rootCmd = consoleserver.NewCommandServer(
"my-app",
"1.0.0",
"My Application",
consoleserver.WithEngineOptions(func(engine *gin.Engine) {
engine.Use(MyMiddleware())
}),
)
You can pass multiple callbacks; they run in order. For WithCommandOptions, custom top-level routes, and longer examples, see Command server options.
Custom middlewareβ
func RateLimiter() gin.HandlerFunc {
limiter := rate.NewLimiter(100, 200)
return func(ctx *gin.Context) {
if !limiter.Allow() {
util.RespondWithError(ctx, util.NewErrorMessage("E4291", "Rate limit exceeded"))
ctx.Abort()
return
}
ctx.Next()
}
}
router.Use(RateLimiter())
Middleware order (mental model)β
At a high level:
- Engine-level stock middleware (recovery, telemetry, logging, CORS, etc.) β see global middleware
- API group: default
WithAuthentication(all routes need a valid session/token unless you useWithoutAuthentication) - Per-route / per-group middleware such as
RequirePermission - Controller handler
Exact ordering is defined in the framework router setup; when in doubt, use the patterns above rather than re-stacking auth manually.
Best practicesβ
- Prefer
WithoutAuthenticationfor explicit public groups instead of bypassing security ad hoc - Use
RequirePermissiononly where business rules need finer control than βlogged inβ - Keep middleware small; call services from controllers
- Always call
ctx.Next()orctx.Abort() - Return errors with
util.RespondWithErrorconsistently
Related documentationβ
- Authentication & Authorization β JWT, OAuth, LDAP, MFA, RBAC naming, service accounts
- Creating controllers β
RegisterRoutesand handler examples - Core concepts: middleware pipeline β where this fits in the big picture
- FAQ: API auth β short answers with links back here