func(http.Handler) http.Handler and securityhttp.NewHandler returns a single
http.Handler, four of the five need no adapter at all.
These live in a separate Go module (
replaced onto the parent) so Gin, Echo,
chi, gorilla/mux, and Fiber never enter the library’s dependency graph — the root
module stays standard library plus golang.org/x/crypto.For the full integration reference — support matrix, per-route middleware
adapters, role-hierarchy caveats — see the
Web Frameworks guide.The integration story, end to end
app.goholds everything the five programs share: two in-memory users (alice/ADMIN,bob/USER), a session store, cookies, and thesecurityhttpoption list.- Each
main.gobuilds its framework’s router and registers four routes. - The chain wraps the whole router —
securityhttp.NewHandler(router, opts...)— so headers, session lookup, CSRF, and default-deny authorization run before the framework routes anything. - Admin access is enforced inside each framework’s own route group, so both enforcement layers appear in one program.
Shared security options
/admin rule here — that check belongs to the framework groups below.
Route patterns are matched against the raw request path, before your framework
parses path parameters, so write them as paths (/admin/**) and never as route
templates (/users/:id, /users/{id}) — those strings never appear in a URL.
One line per framework
app.Listen instead):
frameworks.Principal(ctx) is just core.MustAuthentication(ctx).Name() — the
authentication lives on the request context, so only the way each framework hands
you that context differs.
Gin’s middleware adapter
Gin hasgin.WrapH for handlers but nothing for middleware, so the Gin example
carries a ten-line adapter:
Why Fiber differs
*fiber.App is not an http.Handler, so the chain is mounted as Fiber
middleware and the login endpoint is mounted by hand. Reading the principal still
works: the adaptor copies the request-context values into fasthttp user values,
and *fasthttp.RequestCtx — what c.Context() returns — is itself a
context.Context whose Value reads them back.
Trying it out
Same transcript against every one of the five:bob/hunter2 instead and /admin/panel returns 403 — the framework
route group rejects a caller without ROLE_ADMIN. Posting to /login with no
X-CSRF-Token returns 403 on all five, and every response carries the default
security headers.
Role hierarchy caveat
middleware.RequireRole is a literal authority check — it consults no
role hierarchy. Under ADMIN > STAFF > USER, an
ADMIN caller is denied by RequireRole("STAFF"). For a hierarchy-aware per-route
guard, use a scoped rule set (also middleware, so it drops into the same
adapters):
What these examples prove
- One
securityhttp.NewHandlercall secures Gin, Echo, chi, and gorilla/mux identically — no framework-specific package, no adapter - Framework-native route groups compose with go-security’s guards in every framework, including Fiber
- The authentication is reachable from every framework’s handler, because it travels on the request context
- The cookies here are
Secure: falseso the examples work over plain HTTP on localhost. In production keep the secure defaults and serve TLS.
