Story AUTH-012: Signed-link verification rate-limiting (shared Redis)¶
Module: identity Slice: arch §0.1 AUTH-006 (rate-limit Redis transverse, shared with CP-005 / NOTIF-004) Side: [BACKEND] Version target: V0 — v0.1.0 Priority: 12 Depends on: AUTH-005; the shared Redis rate-limit primitive introduced by CP-005 / NOTIF-004 Can develop concurrently with: AUTH-010, AUTH-011 Merge order: After AUTH-005 Estimated complexity: S PRD User Stories: V0 envelope AUTH-006 (production hardening — rate-limit) Wireframe: N/A — backend only (any throttle message shown to the customer is rendered by the CP module)
Objective¶
The v0.0.x signed-link verification has no throughput protection. v0.1.0 adds a cross-cutting Redis rate-limit on link verification, sharing the same rate-limit primitive that CP-005 and NOTIF-004 introduce. Together with the per-link attempt-limit (AUTH-011), this caps brute-force pressure on the SHA-256 token lookup (which has no HMAC in V0, so rate-limit is the main brute-force defence — arch §0.2).
Backend Scope¶
Entities¶
No new tables. Rate-limit counters live in Redis (shared primitive), not in signed_links.
Service Layer¶
- Apply a Redis-backed rate-limit to
verifyLink(and the challenge submit path) keyed by a coarse client identifier (e.g. IP block) and/or token, reusing the shared rate-limit component from CP-005 / NOTIF-004. - When the limit is exceeded: reject with HTTP 429 (
Retry-After) without consuming a per-link challenge attempt. - The rate-limit is a performance/abuse guard fronting the < 50 ms p95 verification hot path (AUTH-NFR-11); a Redis outage must degrade safely (fail-open on rate-limit is acceptable since the per-link attempt-limit from AUTH-011 and link state remain authoritative — arch R4).
- Optionally trigger link revocation on egregious abuse (defence-in-depth), consistent with the attempt-limit path in AUTH-011.
API Endpoints¶
None new. Applies to the existing CP-boundary verification/challenge calls.
Validation Rules¶
- Rate-limit thresholds and window come from shared configuration (aligned with CP-005 / NOTIF-004).
- A 429 must not consume a per-link attempt (those are independent guards).
Multi-Tenant Considerations¶
Rate-limit keys must not allow one tenant's traffic to exhaust another tenant's budget; key by client + token (which resolves to a single tenant). AUTH stays on the platform datasource; Redis is the shared cache layer.
Audit & Logging¶
- Reuse existing signed-link audit events; excessive-rate rejections may be counted in metrics/observability (the v0.1.0 observability work is an OP-module concern). If abuse triggers a revoke,
AUTH_LINK_PERMANENTLY_REVOKED/AUTH_LINK_REJECTED_REVOKEDapply.
Frontend Scope¶
N/A — backend only. Any "too many requests, retry later" message shown to the customer is rendered by the CP module using the standard low-network / retry copy.
UI States (ALL REQUIRED)¶
N/A — backend only.
Acceptance Criteria¶
AC-001: Excessive verification requests are rate-limited
Given a client exceeds the configured verification rate within the window
When further verify/challenge requests arrive
Then they are rejected with HTTP 429 and a Retry-After hint
And no per-link challenge attempt is consumed by the 429
AC-002: The rate-limit shares the CP-005 / NOTIF-004 primitive
Given the shared Redis rate-limit component
When AUTH applies it to verifyLink
Then it uses the same primitive/configuration as CP-005 and NOTIF-004 (no separate bespoke limiter)
AC-003: Redis outage degrades safely
Given Redis is unavailable
When a verification request arrives
Then the rate-limit fails open (does not block legitimate verification)
And the per-link attempt-limit (AUTH-011) and link state remain authoritative
Compliance Rules¶
- No new compliance article. Rate-limiting supports the CIMA cybersecurity posture (AUTH-OQ-03, open — does not block this story).
- [LEGAL-REVIEW]: authentication mechanism only (red flag #5 unchanged).
Standards & Conventions¶
docs/standards/connectivity-low-bandwidth.md(retry / 429 semantics).docs/standards/java-spring-guidelines.md,docs/standards/api-guidelines.md.docs/standards/critical-rules.md(tenant isolation in rate-limit keys).
Testing Requirements¶
Unit Tests¶
- Over-threshold → 429; a 429 does not consume a per-link attempt.
- Key derivation does not let one tenant exhaust another's budget.
Integration Tests¶
- Redis-backed limiter (Testcontainers Redis): burst of verify calls → 429 after the threshold.
- Redis down → fail-open; verification still works; per-link attempt-limit still enforced.
What QA Will Validate¶
- Verification is throttled under abuse; the limiter is the shared one; a Redis outage does not lock out legitimate customers; tenants are isolated in the rate-limit keyspace.
Out of Scope¶
- The shared rate-limit primitive itself — introduced by CP-005 / NOTIF-004; this story consumes it.
- Expiry — AUTH-010; per-link attempt-limit — AUTH-011.
- HMAC + anti-replay — V1.
- Observability dashboards / metrics for the back-office — OP module (v0.1.0 observability).
Definition of Done¶
- [ ] Redis rate-limit applied to signed-link verification + challenge submit
- [ ] Over-threshold → 429 with Retry-After; no per-link attempt consumed
- [ ] Uses the shared CP-005 / NOTIF-004 rate-limit primitive
- [ ] Redis outage fails open; per-link attempt-limit remains authoritative
- [ ] Rate-limit keyspace isolates tenants
- [ ] Unit + integration tests written and passing
- [ ] Tenant isolation verified (key derivation; platform DB)
- [ ] No silent failures (graceful degradation on Redis outage)