Skip to content

Extend OAuth endpoint address enforcement beyond Go (SPEC §16 req 5–6): umbrella #818

Description

@jeremy

SPEC §16 requirements 5 and 6 — judging the address an advertised issuer, token_endpoint, or device_authorization_endpoint resolves to, at the moment of connection — are [Go-first]: implemented only in Go, via surfguard's dial-time enforcement. Appendix F records the per-SDK state and the seam each SDK would need. This issue tracks extending enforcement to the four remaining SDKs with an OAuth surface (Swift ships no OAuth discovery/device/exchange path and is out of scope).

Why this matters: the attack the requirements close is credential-bearing SSRF — a public issuer whose metadata names a private token_endpoint receives the client_id, device_code, authorization code, client_secret, or refresh token on the SDK's own POST. Requirement 5 (metadata GET, #804) and requirement 6 (credential POSTs, #810) are closed in Go only.

Per-SDK issues (each carries its implementation spec):

Upstream prerequisites (surfguard repo):

Invariants that bind every per-SDK implementation (restated in each issue; normative text lives in SPEC §16 and Appendix F):

  1. Connect-time judgement, not preflight. The address judged MUST be the address connected — a dial hook, a resolved-address pin held through the socket, or equivalent. A classification pass followed by a second, independent resolution is a DNS-rebinding window, not an implementation.
  2. Loopback is trusted operator configuration. Loopback/private admission MUST be an explicit policy option on the SDK's constructors (mirroring Go's AllowLoopback / policy overrides) — never inferred from the metadata-provided endpoint, the request URL, or any other request-derived value, which would recreate the SSRF hole the policy closes.
  3. The three overrides. Each SDK MUST expose a replacement policy, a replacement client/transport for the requests, and a way to disable enforcement (a caller-supplied client counts) — SPEC §16's override contract.
  4. Verdict taxonomy. Refusals are the SDK's typed api_error (or invalid_issuer_origin on the discovery hop), non-retryable, terminating the device poll loop on first refusal; unresolvable and blocked stay distinct verdicts.
  5. Injected clients are the caller's, enforcement included — the policy applies to default transports; redirect suppression (already uniform) applies everywhere.

Definition of done for this umbrella: Appendix F's requirement-5/6 tables show enforcement in all five SDKs, and §16 drops [Go-first].

Metadata

Metadata

Assignees

No one assigned

    Labels

    securitySecurity issue or hardeningspecChanges to the Smithy spec or OpenAPI

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions