fix: WWW-Authenticate hardening, DPoP htu port, docs and conformance#7
Merged
Conversation
WWW-Authenticate / http_status - Security: www_authenticate() sanitizes CR, LF, ", and \ from every interpolated value, closing a header-injection path through attacker-influenced error messages. - DPoPNotSupportedError now emits WWW-Authenticate: Bearer (was DPoP). - http_status(CircuitOpenError) returns 503 (was 500). - www_authenticate() gains keyword-only resource_metadata_url= (RFC 9728) and scope= (RFC 6750), with scope= auto-populated from InsufficientScopeError.required_scopes. - New helpers: response_headers_for() bundles status + WWW-Authenticate into one call; AuthplaneResource.prm_url() returns the RFC 9728 well-known URL. - Both adapter verifiers emit a logging.DEBUG event "authplane.token_verification_failed" with structured error_class / error fields before returning None. DPoP htu host header - Outbound HTTP layer preserves non-default ports in the Host header and brackets IPv6 hostnames, so DPoP-protected requests to authservers on non-standard ports verify under RFC 9449. Docs - Fix 6 snippets that referenced an undefined run_query(); switch URL elicitation example to UrlElicitationRequiredError; rewrite MCP adapter Quick Starts to a single asyncio.run(main()) loop so refresh tasks share the request loop; small inaccuracies removed. Conformance tests - Align RFC 8693 issued_token_type test with the catalog; enforce one-test-per-case_id at collection time (collapses 3 sibling pairs); unify env var to AUTHPLANE_CONFORMANCE_CATALOG; raise a clear error when the catalog file is missing; cleanup (extract repeated SSRF stub, hoist imports). Tests: new coverage in tests/test_errors.py and tests/net/test_ssrf.py; adapter tests cover the new debug log event.
RobertoIskandarani
approved these changes
May 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bundle of fixes and improvements on top of the post-0.1.0 cleanup,
covering WWW-Authenticate / http_status helpers, a DPoP
htuhost-headerbug, doc snippet corrections, and conformance-suite hygiene.
Linked Issue
Changes
WWW-Authenticate / http_status
www_authenticate()now sanitizes CR, LF,", and\from every interpolated value (
realm,error_description,scope,resource_metadata), closing a header-injection path throughattacker-influenced error messages.
DPoPNotSupportedErrornow emitsWWW-Authenticate: Bearer(wasDPoP). The resource is bearer-only by configuration; advertisingthe DPoP scheme misled clients into retries that fail the same way.
http_status(CircuitOpenError)returns503(was500). Thebreaker is structurally equivalent to other temporary-AS-unavailability
errors and should be retryable, not surfaced as an internal error.
www_authenticate()gains keyword-onlyresource_metadata_url=(RFC 9728 §5.1) and
scope=(RFC 6750 §3). Whenscope=is omittedthe helper auto-populates it from
InsufficientScopeError.required_scopes.response_headers_for(error, *, realm, resource_metadata_url, scope)bundles status + WWW-Authenticate into one call.
AuthplaneResource.prm_url()— symmetric withprm_response(),returns the RFC 9728 well-known URL.
logging.DEBUGeventauthplane.token_verification_failedwith structurederror_classand
errorfields before returningNone. Wire behaviour isunchanged.
DPoP
htuhost headerHostheader and brackets IPv6 hostnames per RFC 3986 §3.2.2, so
DPoP-protected requests to authservers on non-standard ports
(e.g.
localhost:9000) verify under RFC 9449. Previously the portwas stripped and the AS reconstructed
http://host/<path>, returninginvalid_dpop_proof.Docs
run_query()with runnable stubs in 6 snippets(README, llm-full.txt, both adapter user guides).
UrlElicitationRequiredError(with a fallback for the no-
consent_urlpath).asyncio.run(main())loop so JWKS / metadata refresh tasks share the loop that handles
requests. The previous
asyncio.run(authplane_mcp_auth(...))+sync
mcp.run()pattern bound the refresh tasks to a loop thatclosed before
mcp.run()started a new one.DPoPProviderwith the requiredDPoPKeyMaterialin the democlient.
scopes_supportedasymmetry.resource_signing_alg_values_supportedline from thePRM example.
Conformance tests
issued_token_typetest with the catalog(accept and surface
access_token, not just rejectjwt).case_idat collection time and collapse threesibling pairs that previously violated this; prevents silent overwrites
in the rolled-up report.
AUTHPLANE_CONFORMANCE_CATALOGacrossconftest.py,test_catalog_alignment.py,CONTRIBUTING.md, and the CI / releaseworkflows.
RuntimeErrorwhen the catalog file ismissing (was: silent fallback to a non-existent default path).
_stub_ssrf_post(...)helper (~120 lines of duplication removed);hoist test-local imports to module top.
Affected Packages
authplane-sdk(root)authplane-mcpauthplane-fastmcpTest Plan
Verified locally before opening this PR:
ruff check .— cleanruff format --check .— 85 files formattedpyright— 0 errorspytest tests— 487 passedauthplane-mcppytest tests— 29 passedauthplane-fastmcppytest tests— 35 passedpytest conformance-tests— 104 passed, 1 xfailed (xfail unchanged)CI will re-run all of these plus coverage.
Checklist
ruff check .passesruff format --check .passespyrightpasses (SDK root)pytestpasses for affected packagesCHANGELOG.mdentry added under[Unreleased]