1. Rejections are logged. chassis v0.2.0 refused without a log line, so
since #149 an unknown REST caller got silent 401s. v0.7.1 writes
`mcp auth rejected` (reason, remote, token fingerprint, path) and
`mcp auth accepted` per authenticated request. Tested through
buildMux.
2. Fallout: since chassis v0.7 (mcp-chassis#4) NewJWTValidator no longer
fails on OIDC discovery; the issuer resolves lazily and a JWT gets
503-and-retry until it does. #83's boot retry loop therefore never
retried, and its tests failed. buildJWTValidator now builds once and
logs an issuer still unresolved at boot. Behaviour change: a
misconfigured issuer no longer exits the process; JWTs get 503, the
static bearer keeps working. internal/startupretry is now unused.
3. Metrics: authenticated REST requests were already labelled by route
(the REST mux sets Pattern on the same *http.Request the middleware
reads). Requests refused by auth never reached the REST mux and were
labelled path="/". labelRESTPattern now sets the REST route's pattern
before auth; unmatched paths keep "/", so the label set stays bounded.
Closes#155
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The retry landed in 9a232ad but was tested only through startupretry with
fake funcs; nothing exercised chassisauth.NewJWTValidator's real 502 path.
Extracts the main() closure into buildJWTValidator (behaviour unchanged)
and tests it against an httptest issuer: 3x502 then 200 succeeds with 4
discoveries; a persistent 502 stops at exactly the 8-attempt budget with
the last error; cancelling ctx mid-backoff returns context.Canceled.
Flip-verified: Attempts=1 turns all three red.
Lane: risk/medium issue, committed to main because the behaviour change
already shipped in 9a232ad; this is a test plus a pure extraction.
Closes#83
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>