UserRateLimiter had been created in initMiddlewares() + stored on
config.UserRateLimiter but never mounted — dead wiring. Per-user rate
limiting was silently not running anywhere.
Applying it as a separate `v1.Use(...)` would fire *before* the JWT
auth middleware sets `user_id`, so the limiter would always skip. The
alternative (add it after every `RequireAuth()` in ~15 route files)
bloats every routes_*.go and invites forgetting.
Solution: centralise it on AuthMiddleware. After a successful
`authenticate()` in `RequireAuth`, invoke the limiter's handler. When
the limiter is nil (tests, early boot), it's a no-op.
Changes:
- internal/middleware/auth.go
* new field AuthMiddleware.userRateLimiter *UserRateLimiter
* new method AuthMiddleware.SetUserRateLimiter(url)
* RequireAuth() flow: authenticate → presence → user rate limit
→ c.Next(). Abort surfaces as early-return without c.Next().
- internal/config/middlewares_init.go
* call c.AuthMiddleware.SetUserRateLimiter(c.UserRateLimiter)
right after AuthMiddleware construction.
Behavior:
- Authenticated requests: per-user limit enforced via Redis, with
X-RateLimit-Limit / Remaining / Reset headers, 429 + retry-after
on overflow. Defaults: 1000 req/min, burst 100 (env-tunable via
USER_RATE_LIMIT_PER_MINUTE / USER_RATE_LIMIT_BURST).
- Unauthenticated requests: RequireAuth already rejected them → the
limiter never runs, no behavior change there.
Tests: `go test ./internal/middleware/ -short` green (33s).
`go build ./...` + `go vet ./internal/middleware/` clean.
Refs: AUDIT_REPORT.md §4.3 "UserRateLimiter configuré non wiré"
+ §9 priority #11.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- ORDER BY dynamiques : whitelist explicite, fallback created_at DESC
- Login/register soumis au rate limiter global
- VERSION sync + check CI
- Nettoyage références veza-chat-server
- Go 1.24 partout (Dockerfile, workflows)
- TODO/FIXME/HACK convertis en issues ou résolus
INT-03: Tests for health endpoint, auth flow, track upload auth,
webhook HTTPS-only, and rate limit headers. Build-tagged
'integration' to avoid running in regular test suite.