Open jrmooring opened 2 years ago
It seems like this runs into many of the same questions we needed to think about in the case of Ads Composed of Multiple Pieces: we don't want to let the different pieces of data intermingle, even inside the Fenced Frame. (One k-anonymous thing shouldn't reveal who the user is, but two independent k-anonymous things sure do!)
I don't see how to solve that problem in the model you're proposing. It doesn't seem like it would work to reuse the above solution and have the templateUrl
embed the various components as sub-Fenced Frames. It seems like you genuinely want the two different k-anonymous thinks truly intermingled, right? — you want the words to come from the Interest Group, but the typeface they're in to come from the native template.
I can't see any way to do that without breaking the privacy, where e.g. a single click inside the Fenced Frame would end up revealing the k-anon information from both the advertiser and the publisher sites.
The privacy problem solved by product-level turtledove is clear to me -- all components are registered at joinAdInterestGroup()
time and could encode the user identity if checked for k-anonymity separately. In this case though, nativeComponents
is checked for k-anonymity as a single entity, not as individual components, and the templateUrl
is specified by the publisher without knowledge of the native components.
In FLEDGE right now we can build a click url https://landing.domain?advertiserInterest=A
where the landing page learns that the user is in k-anonymous interest group A. K-anonymous templateUrl
s would potentially allow https://landing.domain?advertiserInterest=A&publisherDomain=P
, which doesn't seem like a regression from what is possible in a contextual flow (although the additional information flow may not be obvious to users).
To be clear, the typeface is the goal, not click url modification. To prevent this could the native components include an allow list of click URLs? Outgoing links from the fenced frame not present in the allow list are blocked, and could result in the templateUrl being marked abusive, causing a fallback to the contextual flow.
You'd need to jointly check that templateUrl x renderUrl are collectively k-anonymous, otherwise, you could use them jointly to uniquely ID the user. e.g., if there are 10,000 folks, divide them into groups of 100 that use the same renderUrl, and groups of 100 that use the same templateUrl, with only one person using each templateUrl and renderUrl combination. That would let the top-level page's unique per-user ID to be passed into the fenced frame, if the IG is cooperating with the publisher or seller.
That would let the top-level page's unique per-user ID to be passed into the fenced frame, if the IG is cooperating with the publisher or seller.
Suppose that the publisher has to choose the templateUrl without access to adMetadata. Then the selection of the k-anonymous groups between SSP and DSP would be completely independent. Would this mitigate the collective k-anonymity issue?
If so, this could be implemented by separating the templateUrl decision out of scoreAd and into its own method.
I believe we don't want a unique user ID from either the parent page or the page where the IG was joined to be passed into the ad. Since the generateBid() function also can have full context from the publisher page, the bidder and publisher could coordinate their two k-anon URLs to pass the publisher's user ID to the frame (which could then be used to pass along a unique ID on a navigation initiated by the ad - admittedly, we still potentially have that issue with two ad navigations to the same renderURL origin).
We would like to propose other options to support native and outstream video formats in Protected Audience API. The two formats are similar in that both are rendered by seller-provided code. For example,
These use cases are not supported by the current Protected Audience API, where the buyer controls the rendering.
The Seller can set up a Seller Rendering Server. Seller Rendering Server would be responsible for creating the HTML to be run in the Rendering Frame based on buyer-provided assets (such as a native ad definition, VAST video XML, or similar). The Protected Audience API can let the buyer choose whether to use Seller Rendering Server for rendering or not. The seller can indicate the buyer’s preference to Chrome through scoreAd()
.
Buyer hosts a buyerAdAssetUrl
endpoint that will return native assets or VAST video assets
When a buyer joins users to an interest group, the buyer can specify buyerAdAssetUrl
as the renderUrl
.
Seller hosts a Seller Rendering Server endpoint for each format that will return seller’s code during ad rendering for the given format.
When starting a Protected Audience auction, the seller specifies an array of sellerRenderingServers
as part of the auctionConfig
. Each item in the array is a dictionary that includes the creative format name (an arbitrary string from a nomenclature agreed upon between sellers and buyers) and the URL that the seller will use to render the format.
format
is an arbitrary string agreed upon between the buyer and seller.url
.auctionConfig = {
...,
'interestGroupBuyers': ['https://buyer1.com/'],
'auctionSignals': {
'eligibleFormats': ['NATIVE_BANNER', 'OUTSTREAM_VAST_VIDEO']
}
// Proposed new auctionConfig field
'sellerRenderingServers': [{
'format': 'OUTSTREAM_VAST_VIDEO',
'url': 'https://example_render_url.seller.com/render_vast_video',
}, {
'format': 'NATIVE_BANNER',
'url': 'https://example_render_url.seller.com/render_native?template_id=123',
}
],
}
During Protected Audience auction,
generateBid()
’s return value.generateBid() {
// auctionSignals.eligibleFormats contains eligible formats.
...
return {
'bid': bidValue
'ad': {'format': 'OUTSTREAM_VAST_VIDEO'}
}
}
sellerRenderingServers
for rendering in scoreAd()
’s return value. For example, scoreAd() {
...
return {'desirability': ...,
'useSellerRendering': "OUTSTREAM_VAST_VIDEO";
}
useSellerRendering
is set, then the browser creates the final URL to use for rendering by using the useSellerRendering
as key to look up a rendering server URL in the sellerRenderingServers
dictionary. Then the browser attaches buyer’s renderUrl
as a query parameter (&buyerAdAssetUrl=
). For example: https://example_render_url.seller.com/?buyerAdAssetUrl=https%3A%2F%2Fexample_buyer_ad_url.buyer.com%2F
sellerRenderingServers
and buyerAdAssetUrl
.browserSignals.sellerRenderingServer
.reportWin() {
...
// Example browserSignals.sellerRenderingServer
// {
// 'format': 'NATIVE_BANNER',
// 'url': 'https://example_render_url.seller.com/render_native?template_id=123',
// }
}
During ad rendering:
sellerRenderingServers
).buyerAdAssetUrl
to fetch ad assets.
The Seller can provide a JavaScript script. The script would be responsible for constructing the HTML code to be run in the Rendering Frame based on buyer-provided assets (such as a native ad definition, VAST video XML, or similar). The browser would execute the script in a protected “Rendering Worklet” as part of a Protected Audience auction. Buyers and sellers can adjust their bids and scores based on the availability of seller rendering scripts.
Buyer hosts a buyerAdAssetUrl
endpoint that will return native assets or VAST video assets
When a buyer joins users to an interest group, the buyer can use buyerAdAssetUrl
as the renderUrl
.
Seller hosts a static script which implements a generateHTML()
as a global method.
generateHTML()
after the on-device auction to produce the HTML code in the rendering frame.generateHTML()
are:
renderUrl
- Buyer’s renderUrl
.renderUrlAdResponse
- The content of the renderUrl
.generateHTML()
contains a string that represents the HTML code in the rendering frame. export function generateHTML(renderUrl, renderUrlAdResponse) {
// Example implementation for generating HTML for a Video ad
return `<html>
<head>
<script type="text/javascript">
//inlined VastPlayer implementation
</script>
<script type="text/javascript">
const vastPlayer = new VastPlayer();
vastPlayer.loadVast('${renderUrlAdResponse}');
...
vastPlayer.play();
</script>
</head>
<body>
....
</body>
</html>`;
}
When starting a Protected Audience auction, the seller specifies an array of sellerRenderingScripts
as part of the auctionConfig
. Each item in the array is a dictionary that includes the creative format name (an arbitrary string from a nomenclature agreed upon between sellers and buyers) and the url
that will be used to render that format.
format
is an arbitrary string agreed upon between the buyer and seller.url
.auctionConfig = {
...,
'interestGroupBuyers': ['https://buyer1.com/'],
// Proposed new auctionConfig field
'sellerRenderingScripts': [{
'format': 'OUTSTREAM_VAST_VIDEO',
'url': 'https://example_render_url.seller.com/rendering_script',
}]
}
During Protected Audience auction,
generateBid()
’s return value.browserSignals.sellerRenderingScripts
to generateBid()
, so that the buyer can verify the url
that is specified by the seller for a given format.generateBid() {
// browserSignals.sellerRenderingScriptUrls can contain the list of scripts.
...
return {
'bid': bidValue,
'sellerRenderingScriptUrl':
'https://example_render_url.seller.com/rendering_script',
'ad': { ... }
}
browserSignals.sellerRenderingScriptFormat
to scoreAd()
, so that the seller can adjust their score and, perhaps, filter out ineligible ads based on their format.scoreAd() {
// browserSignals.sellerRenderingScriptFormat indicates the buyer's choice.
// The seller could decide whether to accept the bid or not.
...
return {'desirability': ... };
}
renderUrl
. In the same way, the browser could expand the mapping to include the sellerRenderingScriptUrl
of the winning ad.sellerRenderingScripts[].url
and renderUrl.
reportWin()
method, using browserSignals.sellerRenderingScriptFormat.
reportWin() {
...
// Example browserSignals.sellerRenderingScriptFormat
// {
// 'format': 'NATIVE_BANNER',
// 'url': 'https://example_render_url.seller.com/render_native?template_id=123',
// }
}
During ad rendering, the browser constructs the HTML for the ad,
generateHTML()
).renderUrl.
generateHTML(renderUrl, renderUrlResults)
in an isolated worklet (no network connection and disk access).Buyers and sellers may need access to some contextual information in order to obey the regulatory requirements where they operate. This is often in the form of granular collected consent descriptors.
These proposals resolve a major issue with instream video, by enabling publishers to influence the viewers user experience. Additional changes will be needed to support other instream video requirements, such as preloading, state synchronization with the video content and server side ad insertion.
With the Seller Rendering Server approach - if this will eventually need to be in a TEE, would a S2S call even be permitted to fetch the native assets on the backend?
Are there other forums yet where this is being hashed out yet?
For display ads, we can use reportEvent and registerAdMacro functions to be able to fire any events during ad-rendering. For VAST ads it would be good to be able to specify the macros inside the VAST response which were registered in the reportWin method and chrome to be able to substitute them while the vast ad is loading/rendering.
We discussed this idea with the Chrome team and the ad industry on the 2023-12-06 public call. There were two concerns:
To address the above concerns, we propose the following flows:
sellerRenderUrl
into the rendering frame. The sellerRenderUrl
will return the seller’s HTML rendering code with styling. Chrome will make the buyer’s renderUrl
contents accessible to the rendering frame through a predefined API (e.g. global variable or function window.fence.buyerRenderUrlContents
). renderUrl
and sellerRenderUrl
’s HTTP response header.runAdAuction()
), Chrome downloads buyer’s renderUrl
.renderUrl
’s content to a seller provided function generateHTML(renderUrl, renderUrlContent)
and asks the seller to generate the HTML within the rendering frame. The function can be run within an isolated worklet (no storage or network access).renderUrl
’s HTTP response header.Both proposals addressed the above concerns:
renderUrl
’s HTTP response header. Chrome could retrieve the list of allowed ad asset URLs before rendering. During rendering, Chrome could ensure that any network calls from the rendering frame matches the URLs in the pre-declared list. These URLs were declared before the auction; therefore, they do not contain contextual information.renderUrl
and seller’s rendering script / sellerRenderUrl.
renderUrl
and seller’s rendering script.Curious how https://github.com/privacysandbox/privacy-sandbox-demos/pull/242 factors into this revised proposal.
My understanding is that https://github.com/privacysandbox/privacy-sandbox-demos/pull/242 is a short term solution. For example, Step 4 mentioned "The finalized VAST XML is post-messaged out of the creative frame to the video player." The above proposal is intended for long term / post-2026.
Native Advertising
In native advertising a buyer responds with a set of creative assets (title, url, image, ...) which the seller assembles into an ad element that matches the site's styling.
In the FLEDGE world it looks like the best ad-tech could do to match a publisher's design is to have buyers populate an interest group with multiple copies of the same ad:
ads: [{renderUrl: '.../ad1_generic.wbn'}, {renderUrl: '.../ad1_vipPublisher1.wbn'}, ...]
-- this obviously doesn't scale.Proposal
Extending the auction config to take a new optional field,
templateUrl
, could enable retargeted ads in native placements:navigator.runAdAuction({..., templateUrl: 'https://seller.example/publisherA/slotB'})
would allow the auction to return an opaque URL pointing totemplateUrl
instead of arenderUrl
. Like renderUrls, thetemplateUrl
is checked for k-anonymity to make sure it can't decorate hyperlinks with user identifiers.The auction result is used as the src for a fenced frame either way, but in the case that the templateUrl is selected,
navigator.adAuctionNativeComponents()
is available within the fenced frame to retrieve the creative assets of the winning ad.Additional Considerations
nativeComponentsUrl
could be included alongside the renderUrl in ad objects.