a2aproject / a2aproject/a2a-python

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

オープン
#1,197 コメント 1 件 リアクション 0 件 担当者 1 名 @rohityan が担当を希望しています GitHub で見る
component: core
主要言語
Python
スター
2.1k
フォーク
496
平均マージ
4日 17時間
マージ済み PR(30日)
12

説明

### 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

コントリビューションガイド

コントリビューションガイドを開く

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。