Skip to content

[Docs]AP2 challenge flow underdefined at the AP2/A2A contract layer #214

@zesttec

Description

@zesttec

Thanks for publishing AP2. After reading the current main branch, I think there is a small but important protocol-level gap around how transaction challenges are carried between AP2 participants.

AP2 spec says transaction challenges exist and that a successful challenge should help ensure the user is not challenged twice. The problem is that AP2 does not yet appear to define a minimal standard challenge object that all participants can share. Without that, different implementations can encode challenge state differently, so one participant may fail to recognize that another participant has already satisfied the challenge for the same transaction. In practice, that can lead to duplicate or inconsistent challenges, which seems contrary to the spec’s stated intent. (docs/specification.md, roadmap + Section 5.5)

For example, one implementation could treat a local challenge_response field as enough proof that a challenge succeeded, while another could expect a structured result tied to a specific transaction and challenger. If there is no shared challenge identity, transaction binding, status, and completion object, the second participant may not recognize the first result and may trigger another challenge for the same payment.

AP2 v0.1 makes the intended behavior clear. The roadmap mentions “user and merchant-initiated step-up challenges.” Section 5.5 says v0.1 uses a “redirect challenge” on a “trusted surface,” and it says the Credentials Provider should know when a challenge succeeds so the user is “not challenged twice.” (docs/specification.md)

But I could not find a canonical AP2/A2A challenge profile in docs/a2a-extension.md. That file defines transport profiles for IntentMandate, CartMandate, and PaymentMandate, but I could not find a challenge request, challenge status, or challenge completion profile there. In src/ap2/types/mandate.py, I also could not find fields such as challenge_id, challenger, challenge_type, status, expires_at, redirect_url, or completion evidence in the mandate schema I checked.

The current samples seem to reflect this gap. In samples/python/src/roles/merchant_payment_processor_agent/tools.py, the demo reads a free-form challenge_response, builds an ad hoc {"challenge": ...} payload, and validates the OTP locally. In samples/python/src/roles/credentials_provider_agent/tools.py, handle_payment_receipt() is still a placeholder, so the sample does not show how a successful challenge result is recorded or reused. In samples/go/pkg/roles/merchant_payment_processor_agent/executor.go, InitiatePayment() appears to complete payment immediately with a success artifact, without a challenge path.

There also seems to be a spec/sample mismatch. Section 5.5 describes a redirect challenge to a trusted surface, while the Python sample uses an inline OTP-style flow.

Suggested next steps

  • Define a canonical AP2 challenge object and A2A container.
  • Define a small lifecycle, e.g. requested, presented, completed, failed, expired, and optionally reused.
  • Define a challenge completion object with transaction binding and completion evidence.
  • Define how successful challenge outcomes are shared with the Credentials Provider so challenge reuse can work.
  • Update the Python and Go samples to use the same minimal contract.

Thanks again for publishing AP2. Please let me know how you think about it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions