Address second-round review findings: an issuer-mismatched ASM from a
blind eager probe is skipped as failed discovery (falling through to
the {origin}/token fallback) instead of raising out of the auth flow
before the original request is sent; and eager_discovery_attempted is
now set only when the probe sequence completes, so a probe interrupted
by a transport failure is retried on the next refresh rather than
permanently recorded as done.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjbXueCDdFNJK6imejCXgM
Address review findings: without a WWW-Authenticate resource_metadata
hint the eager probes are unanchored, so a co-hosted origin can serve
another resource's documents. Treat a resource-mismatched PRM as failed
discovery instead of raising out of the auth flow; on a SEP-2352
binding mismatch skip the refresh and discard the unanchored discovery
results (including rejected ASM metadata) but keep the credentials for
the anchored 401 path to judge. Run the probes only once per context so
servers publishing no metadata are not re-probed on every in-process
refresh, and finalize the inner refresh generator with aclosing().
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjbXueCDdFNJK6imejCXgM
On a cold start (stored refresh token reused before any 401) the eager
pre-401 refresh built its URL from the urljoin(origin, "/token")
fallback because authorization-server metadata had not been discovered
yet. Servers whose token endpoint lives under a path returned 404, the
client cleared its stored tokens, and headless clients were forced into
an interactive re-auth they cannot perform (#3240, #3250).
Run protected-resource + authorization-server metadata discovery before
the eager refresh so it targets the discovered token endpoint, applying
the same SEP-2352 issuer-binding checks as the 401 discovery path: when
the stored credentials are bound to a different issuer they are dropped
and the refresh is skipped, so credentials are never presented to an
authorization server they are not bound to, and the subsequent 401 flow
re-registers cleanly. Servers publishing no metadata keep the previous
{origin}/token fallback behavior.
Fixes#3240
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjbXueCDdFNJK6imejCXgM
Python's $ with re.match also matches just before a single trailing newline, so tool-name validation accepted "name\n" and UriTemplate.parse accepted varnames like "foo\n". Switch both checks to re.fullmatch.
Closes#3084