package httpserver import ( "time" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/limiter" "anchorage/internal/pkg/auth" ) // SessionLimiter is the brute-force guard on `POST /v1/auth/session`. // Applies only to that specific method+path via Fiber's `Next` skip // function — so you can install it globally with `app.Use(...)` and // have it no-op on every other request. // // perMinute == 0 falls back to 10 attempts per IP per minute. func SessionLimiter(perMinute int) fiber.Handler { if perMinute <= 0 { perMinute = 10 } return limiter.New(limiter.Config{ Max: perMinute, Expiration: time.Minute, Next: func(c *fiber.Ctx) bool { return !(c.Method() == fiber.MethodPost && c.Path() == "/v1/auth/session") }, LimitReached: func(c *fiber.Ctx) error { return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{ "error": "Too Many Requests", "message": "too many session attempts; slow down", }) }, }) } // AnonymousLimiter caps unauthenticated requests per IP across the whole // API. Authenticated requests (those carrying a valid Bearer or session // cookie the BearerMiddleware already resolved) skip the limiter. Probe // paths (/health, /ready, /metrics) are also exempt. // // perMinute == 0 falls back to 120 — a generous bound for a single IP // hitting /openapi.json, /docs, and unauthenticated pre-login traffic. func AnonymousLimiter(perMinute int) fiber.Handler { if perMinute <= 0 { perMinute = 120 } return limiter.New(limiter.Config{ Max: perMinute, Expiration: time.Minute, Next: func(c *fiber.Ctx) bool { // Skip health / metrics — orchestrator probes hit these far // more often than the per-minute bound and we don't want to // DoS ourselves. switch c.Path() { case "/v1/health", "/v1/ready", "/metrics", "/lbz": return true } // Skip authenticated traffic — real API clients burst // legitimately and we don't want to throttle them here. return auth.FromContext(c.UserContext()) != nil }, LimitReached: func(c *fiber.Ctx) error { return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{ "error": "Too Many Requests", "message": "anonymous request rate exceeded", }) }, }) }