a2aproject / a2aproject/a2a-python

[Feat]: Add a runnable sample for Agent Card signing and verification

Abierto
#1,197 1 comentario 0 reacciones 1 asignado Reclamado por @rohityan Ver en GitHub
component: core
Lenguaje dominante
Python
Estrellas
2.1k
Forks
496
Merge medio
4 d 17 h
PR fusionados (30 d)
12

Descripción

### Is your feature request related to a problem? Please describe.

The SDK ships Agent Card signing support in `a2a.utils.signing`
(`create_agent_card_signer` / `create_signature_verifier`), and both
`A2ACardResolver.get_agent_card` and `ClientFactory.create_from_url` accept a
`signature_verifier`. But `samples/` only contains `hello_world_agent.py` and
`cli.py`, so there is no end-to-end example showing how the two halves fit
together.

That leaves a few things easy to get wrong, and all of them fail in ways that
are hard to diagnose:

- **The verifier is a synchronous callable.** The natural implementation of a
`key_provider` — fetch the JWKS from the signature's `jku` with a sync HTTP
client — does blocking I/O inside async code. Against an in-process server it
deadlocks, and the symptom is a bare `InvalidSignaturesError: No valid
signature found`, which points at the crypto rather than at the I/O.
- **`create_agent_card_signer` mutates the card it signs** (it appends to
`card.signatures`). Wiring it directly into
`create_agent_card_routes(card_modifier=...)` means the served card
accumulates a new signature on every request.
- **`key_provider` failures need to be `PyJWTError` subclasses.** The verifier
only catches `PyJWTError` while iterating signatures, so a provider raising
`ValueError` aborts the whole card instead of moving on to the next
signature.
- **Trusting the card's own `jku` defeats the purpose.** The `jku` header lives
in the untrusted card, so a verifier that fetches keys from whatever URL the
card names will happily verify a forged card against the attacker's own key.
The key source has to be pinned out of band, and nothing in the API signature
makes that obvious.

### Describe the solution you'd like

A runnable sample under `samples/` covering both halves of card signing:

- **Server**: sign the Agent Card with an ES256 key via
`create_agent_card_signer`, serve it through
`create_agent_card_routes(card_modifier=...)`, and publish the matching
public key as a JWKS document at `/.well-known/jwks.json` — the URL the
signature's `jku` points to.
- **Client**: verify the fetched card with `create_signature_verifier` passed
to `A2ACardResolver.get_agent_card(signature_verifier=...)`, resolving keys
by `kid` from an allowlist of pinned JWKS URLs, with the accepted algorithms
pinned too.
- **Negative cases**, so the security property is visible rather than asserted:
a card whose transport URL was rewritten in transit, a card with its
signature stripped, and a genuine card whose `jku` the client does not trust.

Plus a smoke test in the style of `tests/integration/test_samples_smoke.py` and
a `samples/README.md` section.

### Describe alternatives you've considered

Documenting the flow in prose only (e.g. in the SDK docs). The unit tests in
`tests/utils/test_signing.py` already cover the crypto, but they use symmetric
keys and an inline `key_provider`, so they do not surface the async, mutation
or key-pinning pitfalls above — those only show up once a real server and
client are wired together.

### Additional context

_No response_

### Code of Conduct

- [x] I agree to follow this project's Code of Conduct

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.