-
Notifications
You must be signed in to change notification settings - Fork 245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Additional Bids and Negative Targeting to the Explainer. #780
Changes from 1 commit
28d940f
8d10a1d
6c5780b
6cbb637
18384ae
2d04688
63da5cc
df5ad8e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,6 +36,14 @@ See [the in progress FLEDGE specification](https://wicg.github.io/turtledove/). | |
- [5.2 Buyer Reporting on Render and Ad Events](#52-buyer-reporting-on-render-and-ad-events) | ||
- [5.2.1 Noised and Bucketed Signals](#521-noised-and-bucketed-signals) | ||
- [5.3 Losing Bidder Reporting](#53-losing-bidder-reporting) | ||
- [6. Additional Bids]() | ||
- [6.1. Auction Nonce]() | ||
- [6.2. Negative Targeting]() | ||
- [6.2.1 Negative Interest Groups]() | ||
- [6.2.2 How Additional Bids Specify their Negative Interest Groups]() | ||
- [6.2.3 Additional Bid Keys]() | ||
- [6.3 HTTP Response Headers]() | ||
- [6.4 Reporting Additional Bid Wins]() | ||
|
||
|
||
## Summary | ||
|
@@ -803,3 +811,184 @@ These signals were requested in [issue 435](https://github.com/WICG/turtledove/i | |
We also need to provide a mechanism for the _losing_ bidders in the auction to learn aggregate outcomes. Certainly they should be able to count the number of times they bid, and losing ads should also be able to learn (in aggregate) some seller-provided information about e.g. the auction clearing price. Likewise, a reporting mechanism should be available to buyers who attempted to bid with a creative that had not yet reached the k-anonymity threshold. | ||
|
||
This could be handled by a `reportLoss()` function running in the worklet. Alternatively, the model of [SPURFOWL](https://github.com/AdRoll/privacy/blob/main/SPURFOWL.md) (an append-only datastore and later aggregate log processing) could be a good fit for this use case. The details here are yet to be determined. | ||
|
||
### 6. Additional Bids | ||
|
||
In addition to bids generated by interest groups, sellers can enable buyers to introduce bids generated outside of the auction. We refer to these as additional bids. Additional bids are commonly triggered using contextual signals. By having more contextual bids participate in the auction, we're reducing the leakage that's learned by detecting whether or not an auction produced an interest-group-based winning ad. | ||
|
||
Buyers compute the additional bids, likely as part of a contextual auction. Buyers need to package up each additional bid using a new data structure that encapsulates all of the information needed for the additional bid to compete against other bids in a Protected Audience auction. Sellers are responsible for passing additional bids to the browser at Protected Audience auction time | ||
|
||
Each additional bid is expressed using the following JSON data structure: | ||
|
||
``` | ||
const additionalBid = { | ||
"bid": { | ||
// Fields analogous to those returned by generateBid() | ||
"ad": 'ad-metadata', | ||
"adCost": 2.99, | ||
"bid": 1.99, | ||
"bidCurrency": "USD", | ||
"render": "https://www.example-dsp.com/ad/123.jpg", | ||
"adComponents": [adComponent1, adComponent2], | ||
"allowComponentAuction": true, | ||
"modelingSignals": 123, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since additional bids are not targeted using IG data, and are not subject to k-anon, will the modelingSignals be noised & bucketed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. For consistency, the values passed into |
||
}, | ||
// These facilitate running reportWin() if this bid wins the auction. | ||
"interestGroup": { | ||
// These are be passed to reportWin() | ||
"owner": "https://www.example-dsp.com" | ||
"name": "campaign123", | ||
// This is used for its definition of reportWin() | ||
"biddingLogicURL": "https://www.example-dsp.com/bid_logic.js" | ||
}, | ||
// Negative interest groups are described in more detail below. | ||
// Only one of negativeInterestGroup or negativeInterestGroups | ||
// should be specified on a given bid. | ||
"negativeInterestGroup": "campaign123_negative_interest_group", | ||
"negativeInterestGroups": { | ||
joiningOrigin: "https://www.example-advertiser.com", | ||
interestGroupNames: [ | ||
"example_advertiser_negative_interest_group_a", | ||
"example_advertiser_negative_interest_group_b", | ||
] | ||
}, | ||
|
||
// The following fields are used to prevent replay of this bid. | ||
// auctionNonce is described in greater detail below. | ||
"auctionNonce": "12345678-90ab-cdef-fedcba09876543210", | ||
// seller and topLevelSeller must match the auction's configuration. | ||
"seller": "https://www.example-ssp.com", | ||
"topLevelSeller": "https://www.another-ssp.com" | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Buyers have so far remained insulated from multi-seller mechanics/identities. If I read this correctly, a buyer's signed additional bid must contain the topLevelSeller. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A |
||
``` | ||
|
||
Additional bids are not provided through the auction config passed to runAdAuction(), but rather through the response headers of a Fetch request, as described in section 6.3 Response Headers. However, the auction config will still have an additionalBids field, whose value will be a Promise with no value, used only to signal to the auction that the additional bids have arrived and are ready to be accepted in the auction. | ||
|
||
``` | ||
navigator.runAdAuction({ | ||
// ... | ||
'additionalBids': promiseFulfilledWhenTheFetchRequestCompletes, | ||
}); | ||
``` | ||
|
||
#### 6.1. Auction Nonce | ||
|
||
To prevent unintended replaying of additional bids, any auction config, whether top-level or component auction config, must include an auction nonce value if it includes additional bids. The auction nonce is fetched provided like so: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fetched and provided? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||
|
||
``` | ||
const auctionNonce = navigator.createAuctionNonce(); | ||
navigator.runAdAuction({ | ||
// ... | ||
'auctionNonce': auctionNonce, | ||
'additionalBids': ..., | ||
}); | ||
``` | ||
|
||
The same nonce value will need to appear in the auctionNonce field of each additional bid associated with that auction config. Auctions that don't use additional bids don't need to create or provide an auction nonce. | ||
|
||
#### 6.2. Negative Targeting | ||
|
||
In online ad auctions for ad space, it’s sometimes useful to prevent showing an ad to certain audiences, a concept known as negative targeting. For example, you might not want to show a new customer advertisement to existing customers. New customer acquisition campaigns most often have this as a critical requirement. | ||
|
||
To facilitate negative targeting in Protected Audience auctions, each additional bid is allowed to identify one or more negative interest groups. If the user has been joined to any of the identified negative interest groups, the additional bid is dropped; otherwise it participates in the auction, competing alongside bids created by calls to generateBid(). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
An additional bid may specify no negative targeting? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. An additional bid that specifies no negative interest groups is always accepted into the auction. I've updated the text here to clarify that. |
||
|
||
##### 6.2.1 Negative Interest Groups | ||
|
||
Negative interest groups are joined using the same API as normal interest groups, though a different set of fields must be provided. Notably, only the owner, name, lifetimeMs, updateURL and additionalBidKey fields are allowed for negative interest groups. Conversely, only negative interest groups are allowed to specify the additionalBidKey field. The 'additionalBidKey' field is described in more detail in section 6.3. Additional Bid Keys. | ||
|
||
``` | ||
const myGroup = { | ||
'owner': 'https://www.example-dsp.com', | ||
'name': 'womens-running-shoes', | ||
'lifetimeMs': 30 * kSecsPerDay, | ||
'updateURL': 'https://www.example-dsp.com/update?id=12345', //optional | ||
'additionalBidKey': 'EA/fR/uU8VNqT3w/2ic4P6Azdaj1J8U35vFwPEf5T4Y=' | ||
}; | ||
navigator.joinAdInterestGroup(myGroup); | ||
``` | ||
|
||
##### 6.2.2 How Additional Bids Specify their Negative Interest Groups | ||
|
||
Additional bids specify the negative interest groups they're negatively targeting against using one of two fields in their JSON data structure - negativeInterestGroup or negativeInterestGroups. | ||
|
||
If an additional bid only needs to specify a single negative interest group, it can do so as follows: | ||
|
||
``` | ||
const additionalBid = { | ||
// ... | ||
"negativeInterestGroup": "example_advertiser_negative_interest_group" | ||
// ... | ||
} | ||
``` | ||
|
||
If an additional bid needs to specify two or more negative interest groups, all of those negative interest groups must be joined from the same site, and that site must be identified ahead of time in the additional bid using the joiningOrigin field: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Both site and origin are used. Which is it? Specifying a site would give adtechs & advertisers flexibility similar to that afforded in ARA where destination site(s) are specified when registering an event. Advertisers may have multiple subdomains/properties where a negative IG could have been tagged. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Point well taken regarding the distinction between site and origin. The restriction on multiple negative interest groups is that they must be from the same origin. I've updated the wording here to make it clearer. |
||
|
||
``` | ||
const additionalBid = { | ||
// ... | ||
"negativeInterestGroups": { | ||
joiningOrigin: "https://example-advertiser.com", | ||
interestGroupNames: [ | ||
"example_advertiser_negative_interest_group_a", | ||
"example_advertiser_negative_interest_group_b", | ||
] | ||
}, | ||
// ... | ||
} | ||
``` | ||
|
||
Any negative interest group that wasn't joined from that identified site won't be considered for negative targeting. This restriction is enforced so that negative targeting can only use targeting data from a single site. An additional bid that only specifies one negative interest group is not subject to the same restriction on joining origin. | ||
|
||
##### 6.2.3 Additional Bid Keys | ||
|
||
We use a cryptographic signature mechanism to ensure that only the owner of a negative interest group can use it with additional bids. Each buyer will need to create a Ed25519 public/secret key pair to sign their additional bids to prove their authenticity, and to regularly rotate their key pairs. | ||
|
||
When a buyer joins a user into a negative interest group, they must provide their 32-byte Ed25519 public key, expressed as a base64-encoded string, via the negative interest group's additionalBidKey field. This can be seen in the example above in section 6.2.1 Negative Interest Groups. The additionalBidKey can be then updated via the negative interest group's updateURL, for example, to enable a buyer to rotate their Ed25519 key pair faster than they could with the expiration of their negative interest groups alone. | ||
|
||
When the buyer issues an additional bid, that bid needs to be signed using their Ed25519 secret key. During a key rotation, the buyer may need to provide a signature of the additional bid with both the old and the new additionalBidKeys while negative interest groups stored on users' devices are updated to the new key. It's for this reason that additional bids may have more than one signature provided alongside the bid. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Does the provision of dual keys 'trigger' a call to the optional There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Negative interest groups are updated at the same time and in the same way as regular interest groups. I've added some text here to clarify. |
||
|
||
If the signature doesn't verify successfully, the additional bid proceeds as if the negative interest group is not present. This "failing open" ensures that only the owner of the negative interest group, who created the additonalBidKey, is allowed to negatively target the interest group, and that nobody else can learn whether the interest group is present on the device. Because the signature check "fails open", buyers should make sure they're using the right keys; for example it might be pertinent to verify a bid signature before submitting the additional bid. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I suppose this implicitly answers the earlier question whether an additional bid may specify no negative IG info. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Hopefully this is clarified by the text added above. |
||
|
||
To ensure a consistent binary payload is signed, the buyer first needs to stringify their additional bid - the JSON data structure seen above. The buyer then generates the necessary signatures and then bundles these together in a JSON structure we'll call the signed additional bid. | ||
|
||
``` | ||
const signedAdditionalBid = { | ||
// "bid" is the result of JSON.stringify(additionalBid) | ||
"bid": "{\"interestGroup\":{\"name\":\"campaign123\"...},...}" | ||
"signatures": { | ||
{ | ||
"key": "9TCI6ZvHsCqMvhGN0+zv67Vx3/l9Z+//mq3hY4atV14=", | ||
"signature": "SdEnASmeyDTjEkag+hczHtJ7wGN9f2P2E...==" | ||
}, | ||
{ | ||
"key": "eTQOmfYCmLL2gqraPJX6YjryU6hW6yHEwmdsXeNL2qA=", | ||
"signature": "kSz0go9iax9KNBuMTLjWoUHQvcxnus8I5...==" | ||
}, | ||
} | ||
} | ||
``` | ||
|
||
Note that the key fields are used by the browser both to verify the signature, and then to authorize the use of those negative interest groups whose additionalBidKey matched keys associated with valid signatures. | ||
|
||
#### 6.3 HTTP Response Headers | ||
|
||
The browser ensures, using TLS, the authenticity and integrity of information provided to the auction through calls made directly to an ad tech's servers. This guarantee is not provided for data passed in runAdAuction(). To account for this, additional bids use the same HTTP response header interception mechanism that's already in use for the Bidding & Auction response blob and directFromSellerSignals. | ||
|
||
To use HTTP response headers to convey the additional bids, the request to fetch them will first need to specify the adAuctionHeaders fetch flag. | ||
|
||
``` | ||
fetch("https://...", {adAuctionHeaders: true}); | ||
``` | ||
|
||
This signals to the browser that it should look for one or more additional bids encoded as HTTP response headers from this Fetch. Each instance of the Ad-Auction-Additional-Bid response header will correspond to a single additional bid. The response may include more than one additional bid by specifying multiple instances of the Ad-Auction-Additional-Bid response header. The structure of each instance of the Ad-Auction-Additional-Bid header must be as follows: | ||
|
||
``` | ||
Ad-Auction-Additional-Bid: | ||
<auction nonce>:<base64-encoding of the signed additional bid> | ||
``` | ||
|
||
These HTTP response headers are intercepted by the browser and diverted to participate in the auction without passing through the JavaScript context. When all of the additional bids for an auction have been received this way, the seller should resolve the additionalBids Promise passed into the auctionConfig that was described in section 6. Additional Bids. The browser will use this as the signal that it's ready to accept the bids provided by the Ad-Auction-Additional-Bid response headers into the auction. | ||
|
||
#### 6.4 Reporting Additional Bid Wins | ||
|
||
For additional bids that win the auction, event-level win reporting is supported, just as it is for bids generated from stored interest groups. However, additional bids have distinct security concerns. The browser can guarantee that bids generated from stored interest groups were, in fact, generated by the buyer's origin, but it cannot provide this same guarantee for additional bids. An additional bid can only win if none of the negative interest groups specified by the additional bid are present on the user's device, and so the signature validation described in section 6.2.3 Additional Bid Keys above also can't help with this. To ensure that buyers understand that they're being asked to report on an additional bid, which cannot be verified having been generated by the buyer's origin, the reporting for additional bids is done by reporting scripts with different names. Instead of `reportWin()` and `reportResult()`, the browser calls functions named `reportAdditionalBidWin()` and `reportAdditionalBidResult()`, respectively, to report a winning additional bid. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a copy/paste remnant? If not, then how are ad components used in add'l bids?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was intentionally copied from 3.2 On-Device Bidding, alongside the other parts of the bid. Ad components for additional bids are handled the same way they are for bids created by a call to
generateBid()
, except that, because there's no stored InterestGroup associated with the additional bid, the restriction that each adComponent must match a value from the InterestGroup's adComponents field doesn't apply. I've added some clarification in the text below.