feat: per-integration and per-user rate limiting (US3) + polish
Implements tasks T026-T032 from specs/002-saas-integration/tasks.md (User Story 3, P3 - the final piece of this feature) plus Polish. - New bespoke Redis fixed-window counter (checkRateLimit, src/infrastructure/cache/rate-limiter.ts) rather than @fastify/rate-limit's default onRequest-stage hook -- that hook runs before this feature's preHandler-based auth resolves the integration/user identity the limit needs to key on. A second preHandler (checkIntegrationRateLimit) runs after authenticateProductIntegration on the inbound route, checking the integration-level limit then the per-user limit independently, each throwing the existing RateLimitError (429 RATE_LIMIT_EXCEEDED) on breach. - New integration test (inbound-rate-limit.test.ts) verifies both limits are enforced independently against a real Postgres/Redis: a throttled user doesn't affect others, and the integration cap throttles even when no individual user has hit their own limit. - Docs: contracts/quickstart updated from the placeholder "RATE_LIMITED" code to the actual reused RATE_LIMIT_EXCEEDED code; cleaned up a duplicated paragraph in the admin endpoints section; added a "SaaS Integration" section to README.md documenting the inbound contract, admin routes (and their known auth-stub limitation), and how rate limits are configured. All 32 tasks in tasks.md are now complete -- all three user stories (P1 trust boundary, P2 admin lifecycle, P3 rate limiting) are implemented and covered by integration tests verified against a live database, in addition to unit tests for the crypto/token primitives. Full quality gate (typecheck/lint/format/architecture/ unit tests) passes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
3e2a5b97a3
commit
55253287b3
@@ -30,7 +30,7 @@ crypto verification or a database lookup on a request that's malformed anyway:
|
||||
9. **`Product.status == 'active'`** → else `403 PRODUCT_INTEGRATION_SUSPENDED`.
|
||||
10. **`tenantId`/`userId` fall within `ProductIntegration.allowedScope`** → else
|
||||
`403 REQUEST_OUT_OF_SCOPE`.
|
||||
11. **Rate limit (integration-level, then user-level) not exceeded** → else `429 RATE_LIMITED`
|
||||
11. **Rate limit (integration-level, then user-level) not exceeded** → else `429 RATE_LIMIT_EXCEEDED`
|
||||
(User Story 3 — applied after auth succeeds, on the resolved integration/user identity).
|
||||
|
||||
Only after all eleven checks pass does `request.reqContext` get populated
|
||||
@@ -78,6 +78,3 @@ product-integration signed-token auth this contract otherwise describes. **Known
|
||||
verification (it exists as scaffolding — see `src/modules/identity/auth`, itself unimplemented).
|
||||
These admin endpoints are therefore not actually access-controlled yet; real JWT verification is
|
||||
a separate, pre-existing gap this feature surfaces but does not fix.
|
||||
|
||||
All five are admin-authenticated via the existing human/admin JWT `auth.plugin.ts` — a separate
|
||||
concern from the product-integration signed-token auth this contract otherwise describes.
|
||||
|
||||
Reference in New Issue
Block a user