Auth & Security Overview
pidgn ships with a complete suite of authentication and security middleware that you can compose together to protect your application. Every middleware follows the same pattern: a comptime configuration struct, a function that returns a HandlerFn, and data passed between middleware via the ctx.assigns key-value store.
Available middleware
Section titled “Available middleware”| Middleware | Function | Purpose |
|---|---|---|
| Bearer token | pidgn.bearerAuth(.{}) | Extracts Authorization: Bearer <token> into assigns |
| Basic auth | pidgn.basicAuth(.{}) | Extracts Authorization: Basic <credentials> into assigns |
| JWT (HMAC-SHA256) | pidgn.jwtAuth(.{}) | Verifies JWT signature and extracts payload into assigns |
| Sessions | pidgn.session(.{}) | Cookie-based session management with an in-memory store |
| CSRF protection | pidgn.csrf(.{}) | Per-session CSRF tokens; validates unsafe HTTP methods |
| Rate limiting | pidgn.rateLimit(.{}) | Token-bucket rate limiter keyed by client header |
How they compose
Section titled “How they compose”Middleware runs in the order you declare it. Some middleware depends on others being earlier in the pipeline. The recommended ordering is:
const App = pidgn.Router.define(.{ .middleware = &.{ pidgn.errorHandler(.{ .show_details = true }), pidgn.logger, pidgn.bodyParser, pidgn.session(.{}), // must come before csrf pidgn.csrf(.{}), // must come after session }, .routes = routes,});Key ordering rules:
- Session before CSRF — the CSRF middleware reads and writes tokens through session assigns. If there is no session middleware upstream, CSRF tokens will not persist across requests.
- Body parser before CSRF — for POST requests, the CSRF middleware checks form fields via
ctx.formValue(), which requires the body parser to have run first. - Auth middleware on scopes — bearer, basic, and JWT middleware are typically applied per-scope rather than globally, so only protected routes require credentials.
Scoped vs global middleware
Section titled “Scoped vs global middleware”Global middleware is declared in the top-level Router.define config and applies to every route. Auth middleware is usually applied to a subset of routes using Router.scope:
const routes = pidgn.Router.scope("/api", &.{ pidgn.bearerAuth(.{ .required = true }),}, &.{ pidgn.Router.get("/profile", profileHandler), pidgn.Router.get("/settings", settingsHandler),});You can nest multiple scopes with different auth strategies:
const routes = pidgn.Router.scope("/auth", &.{pidgn.bearerAuth(.{ .required = true })}, &.{ pidgn.Router.get("/bearer", bearerHandler), }) ++ pidgn.Router.scope("/auth", &.{pidgn.basicAuth(.{ .required = true })}, &.{ pidgn.Router.get("/basic", basicHandler), }) ++ pidgn.Router.scope("/auth", &.{pidgn.jwtAuth(.{ .secret = "my-secret", .required = true })}, &.{ pidgn.Router.get("/jwt", jwtHandler), });The assigns pattern
Section titled “The assigns pattern”All auth middleware stores extracted data in the context assigns map, a fixed-size key-value store available to downstream handlers:
fn protectedHandler(ctx: *pidgn.Context) !void { // Read data placed by auth middleware const token = ctx.getAssign("bearer_token") orelse { ctx.text(.unauthorized, "No token"); return; };
// Use the token to look up the user, etc. _ = token; ctx.json(.ok, "{\"status\":\"authenticated\"}");}Each middleware uses a configurable assign key so you can avoid collisions if you combine multiple strategies on the same route.
Required vs optional mode
Section titled “Required vs optional mode”Every auth middleware has a required field (default false):
required = false— the middleware extracts credentials if present but callsctx.next()regardless. Downstream handlers check assigns to decide what to do.required = true— the middleware returns401 Unauthorizedwith the appropriateWWW-Authenticateheader and does not callctx.next()if credentials are missing or invalid.
Next steps
Section titled “Next steps”- Bearer and Basic Auth — detailed configuration and usage
- JWT Authentication — token verification, claims, and secrets
- Sessions and CSRF — session stores and cross-site request forgery protection
- Rate Limiting — throttle requests per client