Module adcp.decisioning.brand_authz_gate
Tier 3 brand-authorization dispatch gate.
Composes the :class:BrandAuthorizationResolver Protocol
(landed in v3-identity stages 1-3, issue #350 PR #770) with the
framework's dispatch path so adopters can opt into per-brand
authorization the same way they opt into Tier 2 commercial-identity
gating today (via serve(buyer_agent_registry=...)).
The gate runs after Tier 2 buyer-agent resolution and
:meth:AccountStore.resolve, and before the platform method
executes. By that point the framework has:
- a verified, registry-recognized :class:
BuyerAgentwith an active status (else Tier 2 already raised), and - a resolved :class:
Accountthe request operates on.
What the gate is not: it is not the verifier-side
request_signature_* rejection path (that lives in
:mod:adcp.signing.key_origins for the JWKS-origin check and in the
forthcoming verifier integration for the brand_json chain checks —
issue #776). This dispatch-layer gate emits the cross-cutting
PERMISSION_DENIED auth-denial code regardless of credential kind,
matching the Tier 2 unrecognized-agent rejection surface. Per-purpose
spec-shape codes are reserved for the verifier path.
The gate is opt-in. Adopters wire it by passing both
brand_authz_resolver and brand_identity_resolver to
:func:serve(). Wiring one without the other raises
at boot — partial wiring is almost always a misconfiguration: a
resolver without an extractor never has a brand to check, an extractor
without a resolver never has anything to do.
Global variables
var BrandIdentityResolver-
Adopters in
'derived'resolution mode typically derive the brand frombuyer_agent.allowed_brandsor the agent's onboarding record; adopters in'explicit'mode readaccount.metadatapopulated by their :meth:AccountStore.resolve. Either path is fine — the framework does not constrain the source, only the return shape.
Classes
class BrandAuthorizationGate (resolver: BrandAuthorizationResolver,
extract_identity: BrandIdentityResolver)-
Expand source code
@dataclass(frozen=True) class BrandAuthorizationGate: """Bundle of the Tier 3 dependencies — the authorization resolver plus the brand-identity extractor. Passed to the dispatch path as a single immutable bundle so the pairing rule (both wired or neither) holds by construction: constructing the bundle requires both, so the framework only ever sees a complete configuration or no configuration at all. """ resolver: BrandAuthorizationResolver extract_identity: BrandIdentityResolverBundle of the Tier 3 dependencies — the authorization resolver plus the brand-identity extractor.
Passed to the dispatch path as a single immutable bundle so the pairing rule (both wired or neither) holds by construction: constructing the bundle requires both, so the framework only ever sees a complete configuration or no configuration at all.
Instance variables
var extract_identity : BrandIdentityResolvervar resolver : BrandAuthorizationResolver
class BrandIdentity (domain: str, id: str | None = None)-
Expand source code
@dataclass(frozen=True) class BrandIdentity: """The brand a request operates against — the input to the Tier 3 binding check. Mirrors the brand identifier shape carried on wire :class:`AccountReference` natural-key references (``account.brand.domain``) and the spec-defined ``brand_id`` field on ``brand.json/authorized_operators[].brands[]``. :param domain: The brand's domain (e.g. ``"nike.com"``). Required — eTLD+1 binding cannot proceed without it. :param id: Optional brand identifier from the seller's portfolio. When set, the operator-delegation check is scoped to this brand (``authorized_operators[i].brands[]`` must contain this ``id`` or ``"*"``). When ``None``, only ``"*"``-scoped operators satisfy the delegation check — fail-closed on unscoped delegation per :class:`BrandJsonAuthorizationResolver`. """ domain: str id: str | None = NoneThe brand a request operates against — the input to the Tier 3 binding check.
Mirrors the brand identifier shape carried on wire :class:
AccountReferencenatural-key references (account.brand.domain) and the spec-definedbrand_idfield onbrand.json/authorized_operators[].brands[].:param domain: The brand's domain (e.g.
"nike.com"). Required — eTLD+1 binding cannot proceed without it. :param id: Optional brand identifier from the seller's portfolio. When set, the operator-delegation check is scoped to this brand (authorized_operators[i].brands[]must contain thisidor"*"). WhenNone, only"*"-scoped operators satisfy the delegation check — fail-closed on unscoped delegation per :class:BrandJsonAuthorizationResolver.Instance variables
var domain : strvar id : str | None