swarm-queue-client: trim token_request comment block under the 30-line lint

The scope-parameter addition pushed the doc comment on token_request to 39
lines. Moved the HTTP-Basic incident story and the audience/scope rationale
to the crate README's new "Token request shape" section (docs/ is markdown,
exempt from the lint); the code comment keeps the pointer plus the one-line
summary of the invariant. Refs #4464
This commit is contained in:
atlas 2026-09-17 10:01:11 +02:00
commit d9d6d37951
2 changed files with 44 additions and 33 deletions

View file

@ -460,42 +460,18 @@ fn parse_token_response(status: reqwest::StatusCode, body: &str) -> Result<Cache
/// Build the token request: `client_credentials`, authenticated with HTTP
/// Basic.
///
/// 🩸 **The credentials go in the `Authorization` header, not the form body.**
/// Both are legal OAuth 2.0 — `client_secret_basic` and `client_secret_post` —
/// but a client registration names *one*, and authelia's default (and ours) is
/// Basic. Sending them in the body got every token request refused with
/// `Client authentication failed … the registered client is configured to only
/// support 'client_secret_basic'`, which reached the operator as an endless
/// `429` because the retries tripped a rate limiter whose penalty grew faster
/// than the retry interval. The 429 then arrived *before* the credentials were
/// ever evaluated, so the one line naming the real cause appeared once an hour.
///
/// RFC 6749 §2.3.1 says clients SHOULD use Basic, both introspection callers in
/// this workspace already do, and a secret in a header is one fewer place for a
/// proxy to log it.
/// 🩸 **The credentials go in the `Authorization` header, not the form
/// body.** A client registration names one flavor, and authelia's default
/// (and ours) is Basic — the other one once produced an hour of misleading
/// `429`s before the real cause (every request refused at the auth step)
/// surfaced. `audience` and `scope` are both opt-in, RFC 8707 /
/// registered-scope territory; `None` for either reproduces the request
/// this crate sent before the parameter existed. See the crate README's
/// "Token request shape" section for the incident, the RFC citations, and
/// why each parameter exists.
///
/// Split out of [`mint_token`] so the request's *shape* is testable without a
/// running identity provider — see the tests at the bottom of this file.
///
/// `audience` is RFC 8707 resource-indicator territory, and it is opt-in:
/// omitted, this identity gets whatever audience authelia defaults a
/// scopeless `client_credentials` grant to (the queue connection's own
/// case — it has always worked without asking). A caller proving this
/// identity to a SPECIFIC audience-checked receiver — the swarm-otel
/// `oidc/swarm` authenticator being the first one — has to ask for it by
/// name, the same way `swarm-otel.nix`'s own prometheus scrape config
/// already does per target (`endpoint_params.audience`): a token minted
/// without asking carries `aud: []`, and an audience-checked receiver
/// refuses that just as readily as the wrong one.
///
/// `scope` is opt-in for the same reason and travels with `audience`:
/// registration is not issuance, and the authorisation server grants no scope
/// the client never requested. A caller reaching a destination behind authelia's
/// `/api/authz/auth-request` needs `authelia.bearer.authz` here however
/// completely the client is registered for it — the failure `swarm-otel.nix`
/// records against its own client (a scopeless token refused at
/// introspection with "the requested scope is invalid, unknown, or
/// malformed") is the same one, one layer down.
fn token_request(
http: &reqwest::Client,
cfg: &QueueConfig,