Skip to content

JWKS rotation, identifier gates, and authserver 0.2.0 alignment - #28

Open
RobertoIskandarani wants to merge 1 commit into
mainfrom
port/labs-sync
Open

RobertoIskandarani wants to merge 1 commit into
mainfrom
port/labs-sync

Conversation

@RobertoIskandarani

Copy link
Copy Markdown
Contributor

Brings main up to the current development line ahead of the 0.5.0 cut. The [Unreleased] section of CHANGELOG.md is the authoritative list of what changed for a caller; this body covers the mechanics a reviewer needs.

What is in it

  • JWKS and metadata freshness. A rotated jwks_uri is followed on ordinary verification traffic rather than only when the rotation also introduces a new kid, and jwks_uri is resolved per key-set fetch instead of captured at construction. A failed document refresh backs off for max(1, min(30, refresh_seconds)) instead of being retried by the next reader, which previously made every caller pay a full HTTP timeout serialized behind the fetch lock.
  • Identifier gating at construction. Resource and issuer identifiers must be absolute URLs with a scheme and a host; a fragment or userinfo is rejected where the operator wrote it, not from inside a 401 response path.
  • DPoP. The RFC 3986 ;params segment survives htu normalisation (RFC 3986 §3.3 places it in the path; RFC 9449 §4.3 removes only query and fragment), and an IPv6 authority is re-bracketed. Both were cases where a proof this SDK minted compared unequal to itself.
  • Disclosure. Internal error messages and the DPoP nonce no longer reach the WWW-Authenticate challenge.
  • AS-hosted PRM. resource_metadata can point at a document the authorization server hosts.
  • authserver 0.2.0. access_denied and invalid_target are typed and excluded from the circuit breaker shared with introspection — five policy refusals used to open it. An introspection active:false following a locally valid JWT is logged rather than silently read as a revocation.
  • Conformance catalog pin moves to 583a6d9.

Two deliberate carry-forwards

Both are places where this branch keeps what main already had rather than taking the development line’s copy:

  1. publish-pypi.yml keeps the gh-action-pypi-publish v1.14.2 pin. The development line is still on v1.14.0, which does not upload metadata 2.5 wheels — taking it would have reverted the fix and broken the release this port exists to enable.
  2. Three [Unreleased] bullets are condensed to one line each, matching the shape the rest of the family writes. No migration clause was dropped; the mechanism each one described is in the source comments and in this body.

Verification

ruff + pytest green locally (930 passed) with the conformance suites driven against the pinned catalog 583a6d9, which is what CI reads.

Brings main up to the current development line. The CHANGELOG's
[Unreleased] section is the authoritative list; the themes are:

- A rotated jwks_uri is followed on ordinary verification traffic, and
  jwks_uri is resolved per key-set fetch rather than captured at
  construction. A failed document refresh now backs off instead of being
  retried by the next reader.
- Resource and issuer identifiers are gated at construction: absolute URL
  with a scheme and a host, no fragment, no userinfo.
- DPoP: the RFC 3986 ;params segment survives htu normalisation, and an
  IPv6 authority is re-bracketed. Neither is cosmetic — both made proofs
  compare unequal to themselves.
- Internal error messages no longer reach the WWW-Authenticate challenge.
- resource_metadata can point at an AS-hosted PRM document.
- authserver 0.2.0: access_denied and invalid_target are typed and kept
  out of the circuit breaker, and an introspection active:false following
  a locally valid JWT is logged rather than silently read as revoked.
- The conformance catalog pin moves to 583a6d9.

Two files keep what main already had rather than taking the development
line's copy: publish-pypi.yml keeps the gh-action-pypi-publish v1.14.2
pin (v1.14.0 does not upload metadata 2.5 wheels), and three CHANGELOG
bullets are condensed to one line each to match the family's shape.
@RobertoIskandarani
RobertoIskandarani requested a review from a team as a code owner September 28, 2026 22:07
assert claims.kid == "key-v1"
# Still inside the refresh interval, so no second read: the hop on the
# verify path is TTL-gated, not a fetch per verification.
assert metadata_calls == 1
# translate it to the shape a reader expects from a failing import.
try:
_resolve_elicitation_id_kwarg(ElicitRequestURLParams)
_ELICITATION_ID_KWARG = _resolve_elicitation_id_kwarg(ElicitRequestURLParams)
# translate it to the shape a reader expects from a failing import.
try:
_resolve_elicitation_id_kwarg(ElicitRequestURLParams)
_ELICITATION_ID_KWARG = _resolve_elicitation_id_kwarg(ElicitRequestURLParams)
from pydantic import BaseModel

from authplane_mcp import url_elicitation
import authplane_mcp.url_elicitation as url_elicitation
from pydantic import BaseModel

from authplane_fastmcp import url_elicitation
import authplane_fastmcp.url_elicitation as url_elicitation
self, base_url: str, resource: str, *, scopes: list[str] | None = None
) -> AuthplaneTokenVerifier:
"""Build a verifier for ``resource`` against the server at ``base_url``."""
) -> AuthplaneTokenVerifier: ...
await cache.get()
refresh_task = cache._refresh_task # pyright: ignore[reportPrivateUsage]
assert refresh_task is not None
await refresh_task
@property
def jwks(self) -> dict[str, Any]:
"""The single-key JWKS document publishing this key."""
...
Comment thread authplane-fastmcp/tests/test_verifier_dpop_cache.py Dismissed
Comment thread authplane-mcp/tests/test_verifier_dpop_cache.py Dismissed
Comment thread conformance-tests/test_oauth_protocol_conformance.py Dismissed
Comment thread conformance-tests/test_oauth_protocol_conformance.py Dismissed
Comment thread conformance-tests/test_oauth_protocol_conformance.py Dismissed
Comment thread tests/internal/test_jwks_fetcher.py Dismissed
Comment thread tests/internal/test_urls.py Dismissed
Comment thread tests/internal/test_urls.py Dismissed
Comment thread tests/internal/test_urls.py Dismissed
Comment thread tests/verifier/test_verifier_edge_cases.py Dismissed

@muralx muralx left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix bot review comments.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants