Skip to content

spec(auth): require buyer-principal credentials on transport channel; add CREDENTIAL_IN_ARGS#4057

Merged
bokelley merged 2 commits into
mainfrom
bokelley/pr-4046
May 4, 2026
Merged

spec(auth): require buyer-principal credentials on transport channel; add CREDENTIAL_IN_ARGS#4057
bokelley merged 2 commits into
mainfrom
bokelley/pr-4046

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

@bokelley bokelley commented May 4, 2026

Summary

Closes #4046.

The AdCP spec was silent on credential placement. Buyer-principal credentials arrive over the transport's authentication channel — Bearer (RFC 6750 §2), RFC 9421 signature headers, MCP/A2A auth framing (RFC 9728 §3), or mTLS — but nothing said credentials MUST arrive there and MUST NOT be embedded in the task payload. In practice that gap kept producing the same bug class: storefront-shaped adopters independently rediscovered top-level <platform>_access_token, then nested request.context.<platform>_access_token, then request.ext.<platform>_access_token — three rounds of expert review on a single PR each surfacing a different smuggling vector.

This PR adds a normative Credential placement section to authentication.mdx after the existing tenant-resolution paragraph and a new CREDENTIAL_IN_ARGS error code.

What changes

  • docs/building/by-layer/L2/authentication.mdx — new "Credential placement" section. Buyer-principal credentials MUST arrive on the transport's auth channel and MUST NOT live in args (top-level, context, ext, any nested location). Transport-agnostic; applies under every supported mechanism. Carve-outs explicit:
    • push_notification_config.authentication.credentials — webhook receiver auth (orthogonal to the buyer principal).
    • Onboarding-time secrets exchanged out-of-band.
  • Relay subsection — relay topologies (RFC 9421: agency/A2A relay signature model (pass-through vs re-sign) #2324) authenticate under the relay's own principal. Pass-through preserves the brand agent's RFC 9421 signature; re-signing carries brand-agent identity in the request body as identity context. Neither model permits forwarding the brand's transport credential as a relay-side payload field.
  • static/schemas/source/enums/error-code.json — adds CREDENTIAL_IN_ARGS to the canonical enum, with enumDescriptions narrative and enumMetadata.recovery: terminal per the dual-surface pattern. The wire-placement guidance follows the precedent set by CONFIGURATION_ERROR / AGENT_SUSPENDED — the code itself is the discriminator, no error.details shape, error.field carries the path that triggered detection (never the credential value or any prefix), error.message is generic. Sellers MUST drop the smuggled credential from logs/audit/observability before persisting the rejection.
  • docs/building/by-layer/L3/error-handling.mdx — adds CREDENTIAL_IN_ARGS to the Authentication and Access table and a one-paragraph cross-reference next to the AUTH_REQUIRED sub-cases warning, pointing back to the canonical rule in authentication.mdx.

Decisions on the open triage questions

Q1 — Error code (B chosen). New CREDENTIAL_IN_ARGS code with recovery: terminal. The wire-vs-application terminal/correctable mismatch on PERMISSION_DENIED is exactly the foot-gun that bites SDK retry loops; the threat being credential re-logging, the cost of widening the enum is well-paid.

Q2 — Seller rejection strength (B chosen). SHOULD reject under 3.1; upgrades to MUST 90 days after the 3.1 publication date. The security-reviewer's prompt-injection exfil concern is the same threat class security-model.mdx#threats-specific-to-agentic-advertising already flags — weakening to MAY here would contradict that page. The 90-day window gives implementations time to land detection without leaving credentials in LLM-visible payloads during migration.

RFC anchors. Section cites RFC 6750 §2 (Bearer), RFC 9421 §2 (signed requests), and RFC 9728 §3 (protected-resource metadata) so the rule reads transport-agnostic, not bearer-token-specific.

Spec home. authentication.mdx (after the tenant-resolution paragraph at L106), per the protocol-expert recommendation. error-handling.mdx carries the table row + cross-reference only.

Test plan

  • static/schemas/source/enums/error-code.json parses as valid JSON.
  • node scripts/lint-error-codes.cjs — clean (62 canonical codes scanned across 83 storyboards).
  • node scripts/build-schemas.cjs — generated manifest now reports 62 error codes.
  • node scripts/lint-schema-links.mjs --check — clean.
  • node scripts/check-owned-links.js — clean.
  • Pre-commit hook ran the unit suite + typecheck — green.
  • Mintlify navigation / build preview spot-check after CI runs.

🤖 Generated with Claude Code

bokelley and others added 2 commits May 4, 2026 05:09
… add CREDENTIAL_IN_ARGS

Buyer-principal credentials MUST arrive on the transport's authentication
channel (Bearer per RFC 6750 §2, RFC 9421 signature headers, MCP/A2A
auth framing per RFC 9728 §3, or mTLS) and MUST NOT travel in the task
payload — top-level, in `context`, in `ext`, or any nested location. The
spec was previously silent here, so storefront-shaped adopters kept
independently rediscovering the smuggling vectors (top-level, then
`context`, then `ext`).

Adds a `CREDENTIAL_IN_ARGS` error code with `recovery: terminal` —
auto-retry re-logs the credential on each attempt and is itself the
prompt-injection exfiltration surface the rule closes. Sellers SHOULD
reject under 3.1; upgrades to MUST 90 days after the 3.1 publication
date. Carve-outs are explicit: `push_notification_config.authentication.credentials`
is webhook receiver auth (orthogonal to the buyer principal); relay
topologies (#2324) authenticate under the relay's own principal and
MUST NOT forward buyer credentials in args.

Closes #4046.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…credential-placement rule

Per docs-expert review on PR #4057 — the threats-specific-to-agentic-advertising
bullet about "prompt injection exfiltrating agent-side credentials" now points
at the protocol-layer mechanism that closes the buyer-side instance of it
(the credential-placement MUST/MUST NOT in authentication.mdx). One clause,
no restructuring.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley merged commit de60c64 into main May 4, 2026
18 checks passed
@bokelley bokelley deleted the bokelley/pr-4046 branch May 4, 2026 09:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec: clarify that credentials MUST arrive on transport authInfo, never in request args

1 participant