Skip to content

Webhook verifier's expected_adcp_use pin accepts only the deprecated 'webhook-signing' JWK purpose, rejecting the spec-mandated 'request-signing' #1018

Description

@KonstantinMirin

Found while widening our own conformant-receiver test shim to match the AdCP 3.1.1 accept-set for #1291 (salesagent-z6nr.31).

Problem

signing/webhook_verifier.py hard-codes expected_adcp_use=ADCP_USE_WEBHOOK (i.e. the string "webhook-signing") when building VerifyOptions for webhook verification:

# webhook_verifier.py:152
options = VerifyOptions(
    ...,
    expected_adcp_use=ADCP_USE_WEBHOOK,
)

verifier.py's _check_key_purpose (:607-621) then rejects any JWK whose adcp_use isn't byte-equal to that one pinned value, raising REQUEST_SIGNATURE_KEY_PURPOSE_INVALID:

# verifier.py:621
if jwk.get("adcp_use") != expected_adcp_use:
    raise SignatureVerificationError(REQUEST_SIGNATURE_KEY_PURPOSE_INVALID, step=8, ...)

Per AdCP 3.1.1 security.mdx, this is backwards. Step 8 of the verifier checklist reads:

Verify the JWK's use is "sig", key_ops includes "verify", and adcp_use is "request-signing" ... the deprecated "webhook-signing" value MUST also be accepted for backward compatibility. Reject on any other outcome with webhook_signature_key_purpose_invalid.

And :1438 states plainly: "webhook-signing" is DEPRECATED; verifiers MUST still accept it, but new signers SHOULD publish and sign with "request-signing" keys only.

So the accept-set for a webhook verifier is {"request-signing", "webhook-signing"} — never a single value in either direction. As currently written, webhook_verifier.py accepts only the deprecated value and rejects the one the spec directs new signers to use, which is the exact inverse of the intended backward-compatibility behavior.

Suggested direction

Widen the webhook verifier's key-purpose check to accept either ADCP_USE_REQUEST or ADCP_USE_WEBHOOK, matching the request-signing verifier's own (correct) handling, rather than pinning VerifyOptions.expected_adcp_use to a single value. Since expected_adcp_use on VerifyOptions is a single string compared with !=, this likely needs either a small accept-set parameter on VerifyOptions/_check_key_purpose, or the webhook verifier trying both purposes itself.

Workaround in our own tests

We widen our own test-side conformant-receiver shim to attempt "request-signing" first and retry once with "webhook-signing" only on REQUEST_SIGNATURE_KEY_PURPOSE_INVALID, so our own signing keys are graded correctly while this is open.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions