From 0e92faf6157762ecf129eef6fb4397e6cd28ef21 Mon Sep 17 00:00:00 2001 From: saqib mir Date: Fri, 21 Aug 2026 17:39:25 +0530 Subject: [PATCH] docs: spec for SaaS integration & inbound request trust feature /speckit-specify output for 002-saas-integration (roadmap Phase 2): 3 user stories (authenticate/validate inbound requests, admin onboarding/rotation/revocation, rate limiting) and 12 functional requirements. Reserves an idempotency-key field on the inbound contract for the future ticketing feature (docs/11 gap A1) without implementing dedup here. Quality checklist passes with no NEEDS CLARIFICATION markers. Co-Authored-By: Claude Sonnet 5 --- .../checklists/requirements.md | 40 ++++ specs/002-saas-integration/spec.md | 218 ++++++++++++++++++ 2 files changed, 258 insertions(+) create mode 100644 specs/002-saas-integration/checklists/requirements.md create mode 100644 specs/002-saas-integration/spec.md diff --git a/specs/002-saas-integration/checklists/requirements.md b/specs/002-saas-integration/checklists/requirements.md new file mode 100644 index 0000000..454ed83 --- /dev/null +++ b/specs/002-saas-integration/checklists/requirements.md @@ -0,0 +1,40 @@ +# Specification Quality Checklist: SaaS Product Integration & Inbound Request Trust + +**Purpose**: Validate specification completeness and quality before proceeding to planning +**Created**: 2026-08-21 +**Feature**: [spec.md](../spec.md) + +## Content Quality + +- [x] No implementation details (languages, frameworks, APIs) +- [x] Focused on user value and business needs +- [x] Written for non-technical stakeholders +- [x] All mandatory sections completed + +## Requirement Completeness + +- [x] No [NEEDS CLARIFICATION] markers remain +- [x] Requirements are testable and unambiguous +- [x] Success criteria are measurable +- [x] Success criteria are technology-agnostic (no implementation details) +- [x] All acceptance scenarios are defined +- [x] Edge cases are identified +- [x] Scope is clearly bounded +- [x] Dependencies and assumptions identified + +## Feature Readiness + +- [x] All functional requirements have clear acceptance criteria +- [x] User scenarios cover primary flows +- [x] Feature meets measurable outcomes defined in Success Criteria +- [x] No implementation details leak into specification + +## Notes + +- Auth mechanism choice (signed tokens/OAuth2/mTLS) and rotation-window length are deliberately + left to `/speckit-plan`, not decided here — see spec.md Assumptions. +- Idempotency-key enforcement is explicitly deferred to the future ticketing feature (FR-012 + reserves the field only); this is a scope boundary, not a gap. +- Exact rate-limit values and auth-mechanism-per-integration defaults are + `REQUIRES BUSINESS CONFIRMATION` per docs/10-implementation-roadmap.md — not invented here. +- All items pass; no revision iterations were needed. diff --git a/specs/002-saas-integration/spec.md b/specs/002-saas-integration/spec.md new file mode 100644 index 0000000..5c678e1 --- /dev/null +++ b/specs/002-saas-integration/spec.md @@ -0,0 +1,218 @@ +# Feature Specification: SaaS Product Integration & Inbound Request Trust + +**Feature Branch**: `002-saas-integration` + +**Created**: 2026-08-21 + +**Status**: Draft + +**Input**: User description: "Phase 2 of docs/10-implementation-roadmap.md: SaaS integration — +Product/ProductIntegration models, credential validation, service-to-service auth (signed +tokens/OAuth2/mTLS), inbound request contract, rate limiting. Per docs/02-integration-and-security.md." + +## User Scenarios & Testing *(mandatory)* + +### User Story 1 - Every inbound request is authenticated and trusted before anything happens (Priority: P1) + +A registered SaaS product's backend calls SupportHub on a customer's behalf (e.g., a customer +clicked "Help" inside the product). SupportHub validates the calling product's identity, the +credential presented, the product's current status, and the accompanying user/tenant context +against that product's registered integration and its allowed scope — before any downstream +processing occurs. A request from an unregistered product, an invalid/expired/revoked +credential, a suspended product, or context outside the credential's scope is rejected outright. + +**Why this priority**: This is the trust boundary everything else in the system depends on. +Without it, SupportHub cannot safely accept "this is customer X of tenant Y using product Z" as +true, which every later phase (ticketing, AI, orchestration) relies on completely. + +**Independent Test**: Send a request with a valid, correctly-scoped credential and confirm it is +accepted and its product/tenant/user context is trusted; send the same request with an invalid, +expired, or wrong-product credential and confirm it is rejected with no side effects. + +**Acceptance Scenarios**: + +1. **Given** a product has a registered, active integration with a valid credential, **When** it + sends a request with that credential and in-scope context, **Then** the request is accepted + and the product/tenant/user identity it carries is treated as trusted. +2. **Given** a request presents a credential that doesn't match any registered integration, + **When** SupportHub validates it, **Then** the request is rejected and no ticket, session, or + other record is created. +3. **Given** a product's integration status is "suspended," **When** a request arrives for that + product, **Then** it is rejected with a reason distinguishable from "invalid credential" (so + the calling product can tell the difference between "you're not registered" and "you're + registered but temporarily disabled"). +4. **Given** a request includes fields not defined in the inbound contract, **When** it is + validated, **Then** the entire request is rejected rather than the unknown fields being + silently ignored. +5. **Given** a request's tenant/user context doesn't fall within the presented credential's + allowed scope, **When** it is validated, **Then** the request is rejected even though the + credential itself is valid. + +--- + +### User Story 2 - An admin can onboard, rotate, and revoke a product's integration credential (Priority: P2) + +An operator/admin registers a new SaaS product as a SupportHub integration client, issuing it a +credential scoped to that product alone. Later, the admin can rotate that credential (issue a new +one while the old one keeps working for a defined transition window) or revoke it immediately +(e.g., on suspected compromise), without any SupportHub downtime or a deploy. + +**Why this priority**: Without this, User Story 1 has nothing to validate against, and there's no +way to safely respond to a leaked credential — but it's second because a single seeded +integration is enough to prove Story 1 works end to end before onboarding/rotation tooling exists. + +**Independent Test**: Register a new product integration and confirm a request using its +credential is accepted (Story 1); rotate the credential and confirm both old and new credentials +work during the transition, then only the new one after; revoke a credential and confirm the very +next request using it is rejected. + +**Acceptance Scenarios**: + +1. **Given** an admin registers a new product integration, **When** they issue its credential, + **Then** that credential is scoped to that product alone — it is never valid for any other + product's requests. +2. **Given** an active integration, **When** an admin rotates its credential, **Then** requests + using either the old or new credential succeed until the transition window ends, after which + only the new one works. +3. **Given** an active integration, **When** an admin revokes its credential, **Then** the next + request using that credential is rejected, and the revocation is recorded in the audit trail. +4. **Given** any authentication attempt (success or failure) against any integration, **When** it + occurs, **Then** it is recorded in an audit trail an admin can review — including which + integration was involved and the outcome. + +--- + +### User Story 3 - No single product integration or end user can overwhelm the system (Priority: P3) + +Inbound requests are rate-limited both per product integration and per end user within that +integration, using limits an admin can change without a deploy. A product (or a single customer +within it) sending requests far beyond its configured limit is throttled; other integrations and +users are unaffected. + +**Why this priority**: Important for production resilience and fairness across multiple +integrated products, but the system is meaningfully useful (and Stories 1-2 fully testable) +without it — this hardens an already-working trust boundary rather than enabling new behavior. + +**Independent Test**: Send requests from one integration far beyond its configured rate limit and +confirm later requests in the burst are throttled while a concurrent, well-behaved second +integration's requests continue to succeed normally. + +**Acceptance Scenarios**: + +1. **Given** an integration has a configured rate limit, **When** it is exceeded within the + configured window, **Then** further requests from that integration are throttled until the + window resets. +2. **Given** two different end users under the same integration, **When** one exceeds their + per-user limit, **Then** the other user's requests continue to succeed normally. +3. **Given** an admin changes a rate limit value, **When** the change is saved, **Then** it takes + effect without requiring a deploy or restart. + +--- + +### Edge Cases + +- What happens when a credential is presented after its rotation transition window has fully + elapsed? It MUST be treated identically to an already-revoked credential (rejected). +- What happens when the inbound request's signature/token appears valid but is a replay of a + previously-used one (e.g., a captured and resent signed token)? It MUST be rejected — accepted + service-to-service auth mechanisms must be replay-resistant (short-lived tokens with a + nonce/timestamp check, or equivalent). +- What happens when a product has no integration configured at all (never registered)? Requests + MUST be rejected the same way as an invalid credential, without leaking whether the product ID + itself is known to SupportHub. +- What happens when clock skew between the calling product and SupportHub affects a + time-bound signed token's validity window? A small, explicitly bounded tolerance is allowed; + anything beyond it is rejected. +- What happens when an integration is rotated or revoked while a request is mid-flight? The + in-flight request's outcome is decided by validation at the moment it's checked — no partial + application, no race that lets a revoked credential's request complete after revocation is + recorded. +- What happens when the same underlying customer problem is submitted twice in quick succession + (e.g., the calling product's own client retried after a timeout)? Out of scope for this + feature — de-duplicating retried problem reports into a single ticket depends on the `Ticket` + entity, which doesn't exist until the ticketing feature. This feature's contract MUST still + reserve a field for an idempotency key so that later feature can use it without a contract + change (see Assumptions). + +## Requirements *(mandatory)* + +### Functional Requirements + +- **FR-001**: The system MUST require every SaaS product that wants support to be registered as + a distinct integration, each with its own credential — never shared across products. +- **FR-002**: The system MUST validate, on every inbound request before any downstream + processing: the calling product's identity, the presented credential, the product's current + status (active/suspended/deprecated), the accompanying user/tenant context, and the + credential's allowed scope. +- **FR-003**: The system MUST reject a request whose product, tenant, or user values are not + corroborated by the validated integration and its scope — a caller-supplied ID is never trusted + by itself. +- **FR-004**: The system MUST support at least one production-appropriate, replay-resistant + service-to-service authentication mechanism (signed short-lived scoped tokens, OAuth2 + client-credentials, or mTLS), selectable per integration. +- **FR-005**: The system MUST support rotating an integration's credential with a defined + transition window in which both the old and new credential are valid, with zero downtime. +- **FR-006**: The system MUST support revoking an integration's credential with immediate effect + on the next request. +- **FR-007**: The system MUST audit every authentication attempt (success and failure), recording + which integration/credential was involved, without ever recording the raw credential value + itself. +- **FR-008**: The system MUST reject any inbound request containing fields outside the defined + contract, rather than silently accepting or ignoring them. +- **FR-009**: The system MUST rate-limit inbound requests per integration and independently per + end user within an integration, with limit values configurable without a deploy. +- **FR-010**: The system MUST distinguish, in its rejection response, between "unregistered/ + invalid credential," "suspended product," and "out-of-scope request" where doing so does not + leak whether an unregistered product ID exists in the system. +- **FR-011**: The system MUST let an admin change an integration's status (active/suspended/ + deprecated) and have that change take effect on the very next request, without a deploy. +- **FR-012**: The inbound request contract MUST include an optional idempotency-key field, + reserved for the ticketing feature's future use, even though this feature does not implement + deduplication against it. + +### Key Entities + +- **Product Integration**: One SaaS product's registration with SupportHub — its identity, + current status, credential reference, chosen authentication mechanism, allowed scope, and + rotation/revocation timestamps. Exactly one per product; never shared. +- **Customer Reference**: The external user/tenant identifiers a validated request carries, + scoped to the Product Integration that vouched for them — a reference into the SaaS's own + identity system, never a second copy of it (per Constitution Principle I). +- **Authentication Audit Event**: A record of one authentication attempt (success or failure) + against a Product Integration, including outcome and timestamp, but never the raw credential. + +## Success Criteria *(mandatory)* + +### Measurable Outcomes + +- **SC-001**: 100% of inbound requests presenting an invalid, unregistered, expired, or + out-of-scope credential are rejected before any downstream record is created. +- **SC-002**: Revoking a credential stops it from being accepted on the very next request after + revocation — no propagation delay beyond normal request processing. +- **SC-003**: Rotating a credential causes zero failed requests for a well-behaved caller using + either the old or new credential during the transition window. +- **SC-004**: An admin can retrieve a complete authentication audit trail (success and failure) + for any given integration on demand. +- **SC-005**: An integration or user sending requests at 10x its configured rate limit is + measurably throttled while unrelated integrations/users see no change in their own success + rate. +- **SC-006**: Onboarding a new SaaS product as an integration requires no code change or + deploy — it is a configuration/data action only. + +## Assumptions + +- This feature covers the trust boundary and its own admin/audit surface only. It does not + implement ticket creation, the AI agent, or any business logic beyond validating and scoping an + inbound request — those are later features per the roadmap (Phases 3-5+). +- The idempotency key reserved in FR-012 is deliberately not enforced here (no `Ticket` entity + exists yet to deduplicate against) — this is a forward-compatibility placeholder so the + ticketing feature doesn't need a breaking contract change later, per the gap noted in + `docs/11-architect-additions-gaps-and-recommendations.md` §A1. +- "Rate limiting... configurable without a deploy" follows Constitution Principle II + (configuration over hardcoding) — exact default limit values are a + `REQUIRES BUSINESS CONFIRMATION` item per `docs/10-implementation-roadmap.md`, not invented + here. +- Choice of authentication mechanism (signed tokens vs. OAuth2 vs. mTLS) per integration, and the + credential rotation transition-window length, are technical decisions deferred to + `/speckit-plan` — this spec only requires that *a* production-appropriate, replay-resistant + mechanism exists and that rotation/revocation behave as described.