Skip to content

[RES-02-D] Forward-auth and JWKS resilience, failing closed #293

Description

@victornife

Note

Sub-issue of #142 (slice 2b). Depends on: the retry primitive sub-issue.

Scope

forward_auth.url and jwt.jwks_url resolve through the shared primitives: a pool-of-one by default,
an optional named [[upstreams]] for load balancing, and a policy-driven timeout replacing the hardcoded
Timeout: 10 * time.Second in forwardHTTPClient.

Kept as one sub-issue because both share the client construction and the fail-closed rule; splitting them
would separate decisions that must be made together.

Important

Fail closed. When the forward-auth circuit is open, or admission rejects the subrequest, the request
is rejected — never allowed through unauthenticated. A resilience control may never become an
authentication bypass. "Fail open on dependency failure" is a defensible-sounding default elsewhere and
is a critical vulnerability here. Needs an explicit test.

Note

Scope limit. Auth and JWKS inherit transport and TLS resilience only. HTTP application failures
do not trip the breaker in this tranche.
An auth service returning 500 forever is a received response
and never trips it. The two dependencies disagree on what failure even means: JWKS treats
StatusCode != 200 as a refresh failure, forward-auth treats non-2xx as a legitimate application result
(401/403 are the answer). Making 5xx/429 trip a breaker needs a generic outcome-classification
seam — the same seam retry_on needs — so it is one deferred extension with two consumers, not an auth
special case. Do not add an auth-specific second breaker.

Acceptance criteria

  • Both resolve through the shared primitives with a configurable timeout.
  • Fail closed on circuit-open and on admission rejection, with a test.
  • Transport-only breaker scope documented in docs/auth.md.
  • No behaviour change for 2xx/4xx application responses.

Tests

Fail-closed regression; timeout honoured from policy; transport failure trips the breaker; a persistent
500 does not (documented, asserted); JWKS refresh failure path unchanged.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions