You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part of the trust-level roadmap (#16). Prompted by direct feedback from the AP2 team (Yanhe Chen, Google, 2026-06-02): our mandates are AP2 v1-shaped (ap2.PaymentMandate / 0.1-mock), the amount is not cryptographically signed, and deviceAuth/issuerAuth are unverified. The last two are already tracked (#13 does the key-signing swap, #14 the issuer/device trust anchor). This issue is the remaining gap: emitting and verifying the AP2 v2 wire format itself.
Closed PaymentMandate as a terminal KB-SD-JWT: vct: "mandate.payment.1", transaction_id (= sha256 of the checkout JWT), payee, payment_instrument, iat/exp, aud/nonce challenge binding.
payment_amount in ISO 4217 integer minor units (27999 = $279.99) — convert at the mandate boundary; CeremonyOrder uses float dollars today.
Chain wire format: ~~-joined hops, typ: kb+sd-jwt+kb (open, has cnf) vs kb+sd-jwt (closed), sd_hash / issuer_jwt_hash binding to the preceding hop. Candidate base: @sd-jwt/core (OpenWallet Foundation sd-jwt-js); the chain walk is a thin layer on top.
Codegen TS types from the AP2 JSON schemas (same source their pydantic generated/ comes from).
Bypass tests: tampered amount, forged hop key, wrong aud/nonce, broken sd_hash → all refused. Keep the deterministic re-derivation gates (Security invariant 2) as defense in depth.
Out of scope: issuer/device COSE signature verification on the mdoc rails (#14); the underlying key-signing swap (#13); Python cross-SDK conformance (companion issue).
Sequencing: builds on #13 (real keys); #12 consumes the open-mandate constraints; the companion Python-interop issue is the acceptance harness.
Part of the trust-level roadmap (#16). Prompted by direct feedback from the AP2 team (Yanhe Chen, Google, 2026-06-02): our mandates are AP2 v1-shaped (
ap2.PaymentMandate/0.1-mock), the amount is not cryptographically signed, and deviceAuth/issuerAuth are unverified. The last two are already tracked (#13 does the key-signing swap, #14 the issuer/device trust anchor). This issue is the remaining gap: emitting and verifying the AP2 v2 wire format itself.Reference: AP2 Python SDK — SD-JWT / KB-JWT with dSD-JWT delegation chains (
draft-gco-oauth-delegate-sd-jwt-00).Scope (Node/TS,
attestomcp-gate):PaymentMandateas a terminal KB-SD-JWT:vct: "mandate.payment.1",transaction_id(= sha256 of the checkout JWT),payee,payment_instrument,iat/exp,aud/noncechallenge binding.payment_amountin ISO 4217 integer minor units (27999 = $279.99) — convert at the mandate boundary;CeremonyOrderuses float dollars today.~~-joined hops,typ: kb+sd-jwt+kb(open, hascnf) vskb+sd-jwt(closed),sd_hash/issuer_jwt_hashbinding to the preceding hop. Candidate base:@sd-jwt/core(OpenWallet Foundation sd-jwt-js); the chain walk is a thin layer on top.OpenPaymentMandate:AmountRange,AllowedPayees, allowed instruments) — this is AP2 v2's carrier for the human-not-present leg (Human-Not-Present delegation — ap2.IntentMandate (presence: live → delegated · v0.2) #12).PaymentReceipt,reference = sha256(closed leaf JWT).generated/comes from).aud/nonce, brokensd_hash→ all refused. Keep the deterministic re-derivation gates (Security invariant 2) as defense in depth.Out of scope: issuer/device COSE signature verification on the mdoc rails (#14); the underlying key-signing swap (#13); Python cross-SDK conformance (companion issue).
Sequencing: builds on #13 (real keys); #12 consumes the open-mandate constraints; the companion Python-interop issue is the acceptance harness.