Skip to content

docs(spec): one owned outbound HTTP boundary - #33

Draft
antstanley wants to merge 1 commit into
spec/rotate-refresh-tokensfrom
spec/own-outbound-http-boundary
Draft

docs(spec): one owned outbound HTTP boundary#33
antstanley wants to merge 1 commit into
spec/rotate-refresh-tokensfrom
spec/own-outbound-http-boundary

Conversation

@antstanley

@antstanley antstanley commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Note

Stacked PR 9 of 16 — part of stack #35, which targets main.
Base: #22 (spec/rotate-refresh-tokens) · Followed by: #18

This PR's diff shows only its own change. Merge the stack bottom-up; do not merge this before its base.

Why it sits at position 9: Consolidates the two provider validators behind a shared VerificationKeySet — the consolidation #20 explicitly declined — so it must follow the specs that change those validators.

Scope of this PR

This PR currently carries the change spec. It will also carry the implementation plan and the build work for that spec — plan and code land as later commits on this same branch, so the spec, its plan, and its implementation review and merge as one unit.

Review order within the PR: spec first, then plan, then implementation.


Change spec adopting the provider-response-boundary hardening proposal, which a coverage audit found was almost entirely unadopted.

Why it exists. Of the proposal's three "land immediately" items, only the discovery status check had been taken (by #25). Its Option 2 — a shared ProviderTransport and VerificationKeySet — was declined by #20 ("the binding does not move"), leaving four findings with no home and two only half-covered.

Proposed delta. Every provider request goes through a single ProviderTransport (HttpsUrl in, status checked before body, bounded read, UpstreamBody out). Every JWK becomes a verification key only through a single VerificationKeySet whose constructor applies the RFC 7517 §4.2–4.3 purpose filter and carries the resolved algorithm as data — replacing two drifted find_jwk copies, the per-request deep clone, and both per-provider alg matches. The JWKS cache's write guard comes off the network path via an explicit single-flight permit, with a committed clippy.toml configuring await-holding-invalid-types so the pattern cannot return. Outbound, the user-sync webhook's HMAC grows to cover a timestamp and a ULID delivery id, with a receiver contract that obliges freshness and dedup checks.

Findings closed: g2-jwk-selection-oidc, g2-jwk-selection-apple, g2-jwks-cache-lock-across-await, g1-webhook-delivery-replayable. Partials finished: g2-jwks-response-size-unbounded (success path), g2-provider-endpoint-scheme-oidc (origin constraint).

The scan's remediation was wrong, and the spec says so. g2-provider-endpoint-scheme-oidc recommends requiring that jwks_uri, token_endpoint and revocation_endpoint share the issuer's origin. Google's live discovery document puts token_endpoint/revocation_endpoint on oauth2.googleapis.com and jwks_uri on www.googleapis.com — none on the issuer's origin, and eTLD+1 does not rescue it. Implementing that literally breaks the repo's flagship example. The spec instead pins a declared origin set (issuer origin ∪ configured-endpoint origins ∪ an explicit endpoint_origins list), which keeps the real property — a discovery document may confirm origins, never widen them.

Two findings contradict each other, and the ordering resolves it. g2-jwks-response-size-unbounded treats the guard-held-across-fetch as a mitigation bounding concurrent allocation to one body per provider; g2-jwks-cache-lock-across-await treats the same guard as the defect. Fixing the lock removes a bound the other relies on, so the byte ceiling is a hard prerequisite of the single-flight redesign.

Other corrections from reading the code: only two unbounded success sinks remain, not three — token_endpoint.rs reads one raw_body shared by the error and success paths, already routed through read_bounded by #31, so a second read there would be a bug. And migration is five call sites, not the proposal's four: both providers implement revoke_token independently.

Canonical pages: 02-ports-and-adapters.md, 05-provider-system.md, 06-configuration.md, development-guidelines.md, canonical-types.schema.json.

Drafted with the spec-creator plugin from a deep security scan of the repository at 53cbdec9. Spec only — no source changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@antstanley
antstanley force-pushed the spec/own-outbound-http-boundary branch from 9ffc9a2 to 6513a08 Compare August 5, 2026 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant