fastmcp/tests/utilities
Jeremiah Lowin 998b37f32b
Add server-side identity assertion (SEP-990 ID-JAG) (#4483)
* Add server-side SEP-990 identity assertion (ID-JAG)

* Test SEP-990 identity assertion token endpoint

* Format identity assertion test

* Document SEP-990 identity assertion

* Thread identity_assertion through OIDCProxy

* Harden ID-JAG: authoritative scopes and grant-type enforcement

Scopes for the issued token now derive only from the signed assertion (or server policy when it omits scopes); the client-supplied request scope may narrow but never widen them. Enforce the registered grant-type constraint the SDK check bypassed, and have proxy DCR add the jwt-bearer grant to registered clients when identity assertion is enabled.

* Harden ID-JAG: honor nbf, reject non-object payload, bound jti cache, preserve required_scopes

* Document per-process ID-JAG replay limitation and nbf check

* Harden ID-JAG round 3: resource indicator, non-object header, algorithm config

- Honor RFC 8707 resource on the jwt-bearer grant (invalid_target on
  mismatch), mirroring authorize()'s invariant incl. skip-when-unconfigured
- Reject JSON-array JOSE headers with invalid_grant instead of a 500
- Add IdentityAssertion.algorithm so ES256/PS256 issuers can be verified
  (JWTVerifier otherwise defaults to RS256)

* Bind ID-JAG exchange to the assertion's signed client_id and resource

SEP-990: the IdP signs which client and which resource the assertion was
minted for. With public proxy clients the presented client_id is
self-asserted, so the signed binding is what stops client B redeeming
client A's leaked assertion — and the signed resource claim stops an
assertion for server A being redeemed at server B behind the same IdP.

* Harden ID-JAG round 4: check bindings before jti consumption; validate temporal claims, algorithm, and discovery body

- Move the client_id/resource binding checks into the validator itself,
  before jti is recorded as consumed, so an assertion presented with the
  wrong binding is rejected without burning replay protection for whoever
  it actually belongs to
- Reject non-numeric exp/iat/nbf with invalid_grant instead of a 500
- Validate IdentityAssertion.algorithm at config time (must be an
  asymmetric JWS algorithm verifiable via JWKS)
- Reject a non-object OIDC discovery body with invalid_grant instead of a 500
- Centralize the resource-URL comparison helpers used by both the
  validator and OAuthProxy.authorize()

* Rebase onto httpx2/SDK b2 and harden ID-JAG round 5

- Migrate identity assertion + tests to httpx2 and the local httpx2_mock
  (legacy httpx is now banned; pytest-httpx no longer intercepts)
- Add is_optional to the shared httpx2_mock, mirroring pytest-httpx
- Tighten the algorithm allowlist to JWTVerifier's exact supported set
  (prefix check accepted typos like RS999 -> 500 on first exchange)
- Reject non-string jti before the cache lookup (unhashable -> 500)
- Track revocation for self-contained ID-JAG tokens: revoke_token records
  the jti and load_access_token rejects it until natural expiry
- Dedupe resource-URL helpers: proxy now imports the shared
  normalize_resource_url/server_url_has_query from identity_assertion

* Advertise 'none' token-endpoint auth method when ID-JAG is enabled without CIMD

DCR clients are public, so metadata consumers must see 'none' to use the
advertised jwt-bearer grant; previously only the CIMD path added it.

* Document 2026-07-28 protocol support as a distinct feature catalog

SEP-990 identity assertion leads: the SDK provides the wire contract and
provider hook; FastMCP provides the complete server-side implementation.
Inventories the full modern-era capability set for v4.

* Harden ID-JAG round 6: lazy re-export, dual-form audience, per-issuer algorithms, discovery backoff

- IdentityAssertion re-exported lazily from server.auth (the eager import
  bypassed the package's documented lazy-import boundary)
- Accept the ID-JAG aud both with and without base_url's trailing slash;
  metadata advertises the slashed form, so IdPs echoing it verbatim work
- algorithms={issuer: alg} per-issuer override, mirroring jwks_uris
- OIDC discovery serializes per-issuer and backs off 30s after a failure
  (discovery runs pre-signature, so garbage could amplify into HTTP floods)
2026-07-18 19:52:15 -04:00
..
json_schema_type fix: guard recursive refs in json_schema_to_type (#4312) 2026-06-24 11:53:36 -04:00
openapi Migrate to MCP SDK v2.0.0b2 (httpx2) (#4503) 2026-07-18 15:12:47 -04:00
__init__.py restore 1.x code 2025-04-09 11:54:42 -04:00
httpx2_mock.py Add server-side identity assertion (SEP-990 ID-JAG) (#4483) 2026-07-18 19:52:15 -04:00
test_async_utils.py Add targeted coverage tests (#4230) 2026-05-25 18:57:28 -04:00
test_auth.py feat: option to add upstream claims to the FastMCP proxy JWT (#2997) 2026-01-28 15:57:24 -05:00
test_cli.py Fix path resolution in install commands (#1951) 2025-09-29 20:04:12 -04:00
test_components.py Stabilize upgraded ty checks (#4526) 2026-07-17 17:46:53 -04:00
test_docstring_parsing.py Extract parameter descriptions from docstrings (#3872) 2026-04-12 13:43:48 -04:00
test_http.py Add targeted coverage tests (#4230) 2026-05-25 18:57:28 -04:00
test_inspect.py Migrate to MCP Python SDK v2 (#4437) 2026-07-06 17:36:45 -04:00
test_inspect_icons.py Migrate to MCP Python SDK v2 (#4437) 2026-07-06 17:36:45 -04:00
test_json_schema.py Don't mutate the caller's schema in compress_schema (#4492) 2026-07-17 17:29:01 -04:00
test_logging.py Add enable_rich_logging setting to disable rich formatting (#2893) 2026-01-16 20:29:29 -05:00
test_skills.py Migrate to MCP Python SDK v2 (#4437) 2026-07-06 17:36:45 -04:00
test_tests.py Migrate to MCP Python SDK v2 (#4437) 2026-07-06 17:36:45 -04:00
test_timeout.py Add targeted coverage tests (#4230) 2026-05-25 18:57:28 -04:00
test_token_cache.py Extract TokenCache utility, add caching to GitHubTokenVerifier (#3547) 2026-03-18 15:26:09 -04:00
test_typeadapter.py Fix method-bound tools (#1360) 2025-08-04 09:41:05 -04:00
test_types.py Migrate to MCP Python SDK v2 (#4437) 2026-07-06 17:36:45 -04:00
test_version_check.py Migrate to MCP SDK v2.0.0b2 (httpx2) (#4503) 2026-07-18 15:12:47 -04:00