Measured against adcp==6.6.0.
The asymmetry
brand_authz already understands that a brand operates several agents — _find_listed_agents iterates agents[], and there is an explicit branch for "Multiple agents[] entries byte-equal the agent URL."
But the verifier's entry point accepts only one:
VerifyOptions.agent_url: str | None (adcp/signing/verifier.py:148)
brand_authz.check(agent_url: str, ...) (adcp/signing/brand_authz.py:111)
So a verifier can express "I expect this request to come from this one agent," and cannot express "I expect it from any of these agents of the same counterparty."
Why that is a problem for a seller
A seller onboards a counterparty — a brand — and stores what it knows about them. When a request arrives authenticated by a bearer token, the seller wants to verify the accompanying signature against the agents that counterparty is known to operate. With a single-valued agent_url it has to pick one.
The workarounds are both poor:
- Call the verifier once per candidate URL. N JWKS resolutions, N replay-store interactions, and a refusal that has to be synthesised from N partial failures. The replay checks are the sharp edge: a nonce consumed on the first attempt may make later attempts fail for the wrong reason.
- Pass
agent_url=None. The expected-agent check is skipped entirely, which discards exactly the property being verified.
Neither is what a seller wants, and the shape of the data (agents[] is a list) says the library already knows the real cardinality.
What would help
VerifyOptions.agent_url accepting a collection — or a sibling field such as acceptable_agent_urls — with brand_authz.check matching the verified agent against any member and reporting which one matched. BrandAuthorizationResult.matched_agent_url already exists to carry that answer.
If a single expected URL is deliberate and multi-agent verification is meant to be done another way, a note in VerifyOptions' documentation saying so would be just as useful — the current asymmetry between agents[]-as-a-list and agent_url-as-a-scalar reads as an oversight rather than a decision.
Context
Downstream this forces a 1:1 principal↔agent model in a multi-tenant seller, where a brand running staging and production agents, two regional stacks, or migrating endpoints has to be represented as two separate principals — splitting one buyer's identity across credentials, accounts and media buys. Tracked at prebid/salesagent#2276.
Measured against
adcp==6.6.0.The asymmetry
brand_authzalready understands that a brand operates several agents —_find_listed_agentsiteratesagents[], and there is an explicit branch for "Multipleagents[]entries byte-equal the agent URL."But the verifier's entry point accepts only one:
VerifyOptions.agent_url: str | None(adcp/signing/verifier.py:148)brand_authz.check(agent_url: str, ...)(adcp/signing/brand_authz.py:111)So a verifier can express "I expect this request to come from this one agent," and cannot express "I expect it from any of these agents of the same counterparty."
Why that is a problem for a seller
A seller onboards a counterparty — a brand — and stores what it knows about them. When a request arrives authenticated by a bearer token, the seller wants to verify the accompanying signature against the agents that counterparty is known to operate. With a single-valued
agent_urlit has to pick one.The workarounds are both poor:
agent_url=None. The expected-agent check is skipped entirely, which discards exactly the property being verified.Neither is what a seller wants, and the shape of the data (
agents[]is a list) says the library already knows the real cardinality.What would help
VerifyOptions.agent_urlaccepting a collection — or a sibling field such asacceptable_agent_urls— withbrand_authz.checkmatching the verified agent against any member and reporting which one matched.BrandAuthorizationResult.matched_agent_urlalready exists to carry that answer.If a single expected URL is deliberate and multi-agent verification is meant to be done another way, a note in
VerifyOptions' documentation saying so would be just as useful — the current asymmetry betweenagents[]-as-a-list andagent_url-as-a-scalar reads as an oversight rather than a decision.Context
Downstream this forces a 1:1 principal↔agent model in a multi-tenant seller, where a brand running staging and production agents, two regional stacks, or migrating endpoints has to be represented as two separate principals — splitting one buyer's identity across credentials, accounts and media buys. Tracked at prebid/salesagent#2276.