diff --git a/.changeset/fix-hmac-framing-skill-9421-default.md b/.changeset/fix-hmac-framing-skill-9421-default.md new file mode 100644 index 0000000000..6aaf4f9669 --- /dev/null +++ b/.changeset/fix-hmac-framing-skill-9421-default.md @@ -0,0 +1,5 @@ +--- +"adcontextprotocol": patch +--- + +Fix stale HMAC-as-recommended framing in reporting-webhook.json, auth-scheme.json, and create-media-buy-request.json's artifact_webhook; add RFC 9421 default guidance to call-adcp-agent SKILL.md. Description-only fixes aligning these surfaces with the existing push-notification-config.json framing (HMAC is the deprecated fallback, RFC 9421 is the default). No wire format changes. diff --git a/skills/call-adcp-agent/SKILL.md b/skills/call-adcp-agent/SKILL.md index d3da9200f1..c8ae023e6e 100644 --- a/skills/call-adcp-agent/SKILL.md +++ b/skills/call-adcp-agent/SKILL.md @@ -89,6 +89,25 @@ When you see `status: 'submitted'`, the work is NOT complete. Poll via `tasks/ge `budget` is a **number** (not `{amount, currency}` — currency is implied by the pricing option). Required per package: `product_id`, `budget`, `pricing_option_id`. `buyer_ref` is optional but strongly recommended as a buyer-side correlation id across retries and reporting. +### Webhook signing — omit `authentication` for new integrations + +When you include `push_notification_config` in a request, do **not** set `authentication` +unless you are integrating with a legacy receiver. Omitting `authentication` selects the +default: the seller signs each inbound webhook POST with its RFC 9421 +`adcp_use: "webhook-signing"` key, published at the `jwks_uri` in its own `brand.json` +`agents[]` entry. You verify against the seller's JWKS. No shared secret crosses the wire. + +Presence of `authentication` is a **switch, not a fallback** — it opts the seller into +Bearer or HMAC-SHA256 and disables 9421 for that registration. A buyer MUST NOT attempt +"try 9421 first, fall back to HMAC" verification — mode is fixed at registration time. + +The `authentication` block (Bearer / HMAC-SHA256) is deprecated and sellers MAY decline +to support it. It is removed in AdCP 4.0. The `token` field (a correlation token echoed +back in the webhook payload) is separate from `authentication` and is not deprecated. + +See [Webhook callbacks](https://adcontextprotocol.org/docs/building/implementation/security#webhook-callbacks) for the +full verifier checklist and downgrade-resistance rules. + ## Error envelope — read `issues[]` to recover Every validation failure produces: diff --git a/static/schemas/source/core/reporting-webhook.json b/static/schemas/source/core/reporting-webhook.json index cb9a295533..0bc09d5081 100644 --- a/static/schemas/source/core/reporting-webhook.json +++ b/static/schemas/source/core/reporting-webhook.json @@ -17,11 +17,11 @@ }, "authentication": { "type": "object", - "description": "Authentication configuration for webhook delivery (A2A-compatible)", + "description": "Legacy authentication configuration for webhook delivery (A2A-compatible). Opts the receiver into Bearer or HMAC-SHA256 signing. Both schemes are deprecated; the preferred signing profile for new integrations is RFC 9421, where the seller signs with a key published at its brand.json agents[] entry and the buyer verifies against the seller's JWKS — no shared secret crosses the wire (see docs/building/implementation/security.mdx#webhook-callbacks). This field is required in AdCP 3.x; the requirement is removed in AdCP 4.0 when the default RFC 9421 path becomes the only path.", "properties": { "schemes": { "type": "array", - "description": "Array of authentication schemes. Supported: ['Bearer'] for simple token auth, ['HMAC-SHA256'] for signature verification (recommended for production)", + "description": "Array of authentication schemes. ['Bearer'] for simple token auth, ['HMAC-SHA256'] for legacy shared-secret signing. Both are deprecated; new integrations SHOULD use the RFC 9421 webhook signing profile instead.", "items": { "$ref": "/schemas/enums/auth-scheme.json" }, @@ -30,7 +30,7 @@ }, "credentials": { "type": "string", - "description": "Credentials for authentication. For Bearer: token sent in Authorization header. For HMAC-SHA256: shared secret used to generate signature. Minimum 32 characters. Exchanged out-of-band during onboarding.", + "description": "Credentials for the legacy scheme. For Bearer: token sent in Authorization header. For HMAC-SHA256: shared secret used to generate signature. Minimum 32 characters. Exchanged out-of-band during onboarding.", "minLength": 32 } }, diff --git a/static/schemas/source/enums/auth-scheme.json b/static/schemas/source/enums/auth-scheme.json index 1ae1063f76..af841d9d0e 100644 --- a/static/schemas/source/enums/auth-scheme.json +++ b/static/schemas/source/enums/auth-scheme.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "/schemas/enums/auth-scheme.json", "title": "Authentication Scheme", - "description": "Authentication schemes for push notification endpoints", + "description": "Legacy authentication schemes for the webhook auth block. Bearer: token sent in Authorization header. HMAC-SHA256: legacy shared-secret signing. Both are deprecated; new integrations SHOULD omit the authentication block and use the RFC 9421 webhook signing profile (applicable on schemas where authentication is optional). Removed in AdCP 4.0.", "type": "string", "enum": [ "Bearer", diff --git a/static/schemas/source/media-buy/create-media-buy-request.json b/static/schemas/source/media-buy/create-media-buy-request.json index fa5e2607ea..dccf332c7b 100644 --- a/static/schemas/source/media-buy/create-media-buy-request.json +++ b/static/schemas/source/media-buy/create-media-buy-request.json @@ -145,11 +145,11 @@ }, "authentication": { "type": "object", - "description": "Authentication configuration for webhook delivery (A2A-compatible)", + "description": "Legacy authentication configuration for webhook delivery (A2A-compatible). Opts the receiver into Bearer or HMAC-SHA256 signing. Both schemes are deprecated; the preferred signing profile for new integrations is RFC 9421, where the seller signs with a key published at its brand.json agents[] entry and the buyer verifies against the seller's JWKS — no shared secret crosses the wire (see docs/building/implementation/security.mdx#webhook-callbacks). This field is required in AdCP 3.x; the requirement is removed in AdCP 4.0 when the default RFC 9421 path becomes the only path.", "properties": { "schemes": { "type": "array", - "description": "Array of authentication schemes. Supported: ['Bearer'] for simple token auth, ['HMAC-SHA256'] for signature verification (recommended for production)", + "description": "Array of authentication schemes. ['Bearer'] for simple token auth, ['HMAC-SHA256'] for legacy shared-secret signing. Both are deprecated; new integrations SHOULD use the RFC 9421 webhook signing profile instead.", "items": { "$ref": "/schemas/enums/auth-scheme.json" }, @@ -158,7 +158,7 @@ }, "credentials": { "type": "string", - "description": "Credentials for authentication. For Bearer: token sent in Authorization header. For HMAC-SHA256: shared secret used to generate signature. Minimum 32 characters. Exchanged out-of-band during onboarding.", + "description": "Credentials for the legacy scheme. For Bearer: token sent in Authorization header. For HMAC-SHA256: shared secret used to generate signature. Minimum 32 characters. Exchanged out-of-band during onboarding.", "minLength": 32 } },