WICG / turtledove

TURTLEDOVE
https://wicg.github.io/turtledove/
Other
518 stars 219 forks source link

Support for native advertising #265

Open jrmooring opened 2 years ago

jrmooring commented 2 years ago

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 to templateUrl instead of a renderUrl. Like renderUrls, the templateUrl 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.

<html>
    <head><style>...publisher specific styling...</style></head>
    <body>
        <h1 id="title"></h1>
        ...
    </body>
    <script>
        document.getElementById("title").innerText = navigator.adAuctionNativeComponents().title;
        ...
    </script>
</html>

Additional Considerations

michaelkleber commented 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.

jrmooring commented 2 years ago

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 templateUrls 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.

MattMenke2 commented 2 years ago

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.

donato commented 9 months ago

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.

MattMenke2 commented 9 months ago

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).

timphsieh-google commented 9 months ago

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.

Proposal 1: Seller Rendering Server

Overview

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().

Detailed Flow

  1. Buyer hosts a buyerAdAssetUrl endpoint that will return native assets or VAST video assets

    1. If the creative is native, the endpoint should return a JSON that contains native assets (in OpenRTB Native API format).
    2. If the creative is a VAST video, then the endpoint should return a VAST document.
  2. When a buyer joins users to an interest group, the buyer can specify buyerAdAssetUrl as the renderUrl.

  3. Seller hosts a Seller Rendering Server endpoint for each format that will return seller’s code during ad rendering for the given format.

  4. 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.

    1. The format is an arbitrary string agreed upon between the buyer and seller.
    2. If the ad slot is eligible for native ads, then the seller could encode native template that represents the publisher-specified styling settings in this 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',
        }
      ],
    }
  5. During Protected Audience auction,

    1. The buyer can indicate the selected format to the seller in the generateBid()’s return value.
    generateBid() {
      // auctionSignals.eligibleFormats contains eligible formats.
      ...
      return {
        'bid': bidValue
        'ad': {'format': 'OUTSTREAM_VAST_VIDEO'}
      }
    }
    1. Based on the buyer’s response, the seller can choose whether to use one of the sellerRenderingServers for rendering in scoreAd()’s return value. For example,
    scoreAd() {
      ...
      return {'desirability': ...,
              'useSellerRendering': "OUTSTREAM_VAST_VIDEO";
    }
    1. If 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 sellerRenderingServersdictionary. 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
    1. The browser can enforce K-anonymity on the generated URL, which contains both sellerRenderingServers and buyerAdAssetUrl.
    2. At reporting time, the browser can inform a buyer about the selected format in browserSignals.sellerRenderingServer.
    reportWin() {
      ...
      // Example browserSignals.sellerRenderingServer  
      // {
      //  'format': 'NATIVE_BANNER',
      //  'url': 'https://example_render_url.seller.com/render_native?template_id=123',
      // }
    }
  6. During ad rendering:

    1. The browser sends the request to the constructed rendering URL, which points to the Seller’s Rendering Server (sellerRenderingServers).
    2. Seller’s Rendering Server could send a server-to-server request to buyerAdAssetUrl to fetch ad assets.
      1. If the creative is native, then the seller could serve the seller template code embedded with native assets.
      2. If the creative is video, then the seller could return an ad video player with VAST document embedded.

Proposal 2: Seller Rendering Script

Overview

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.

Detailed Flow

  1. Buyer hosts a buyerAdAssetUrl endpoint that will return native assets or VAST video assets

    1. If the creative is native, the endpoint should return a JSON that contains native assets (in OpenRTB Native API format).
    2. If the creative is a VAST video, then the endpoint should return a VAST document.
  2. When a buyer joins users to an interest group, the buyer can use buyerAdAssetUrl as the renderUrl.

  3. Seller hosts a static script which implements a generateHTML() as a global method.

    1. The browser will call generateHTML() after the on-device auction to produce the HTML code in the rendering frame.
    2. The arguments to generateHTML() are:
      1. renderUrl - Buyer’s renderUrl.
      2. renderUrlAdResponse - The content of the renderUrl.
    3. The output of 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>`;
    }
  4. 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.

    1. The format is an arbitrary string agreed upon between the buyer and seller.
    2. If the ad slot is eligible for native ads, then the seller could encode a publisher-specific native template that refers to publisher-chosen styling settings in the url.
    auctionConfig = {
      ...,
      'interestGroupBuyers': ['https://buyer1.com/'],
    
      // Proposed new auctionConfig field
      'sellerRenderingScripts': [{
         'format': 'OUTSTREAM_VAST_VIDEO',
         'url': 'https://example_render_url.seller.com/rendering_script',
      }]
    }
  5. During Protected Audience auction,

    1. The buyer can indicate the selected format to the seller in the generateBid()’s return value.
    2. The browser will pass 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': { ... }
    }
    1. The browser passes browserSignals.sellerRenderingScriptFormatto 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': ... };
    }
    1. When an ad wins an auction, the browser generates a URN which can be mapped to the ads renderUrl. In the same way, the browser could expand the mapping to include the sellerRenderingScriptUrlof the winning ad.
    2. The browser enforces K-anonymity on the combination of sellerRenderingScripts[].urland renderUrl.
    3. The browser can notify the buyer of the selected rendering script in their reportWin() method, using browserSignals.sellerRenderingScriptFormat.
    reportWin() {
      ...
      // Example browserSignals.sellerRenderingScriptFormat  
      // {
      //  'format': 'NATIVE_BANNER',
      //  'url': 'https://example_render_url.seller.com/render_native?template_id=123',
      // }
    }
  6. During ad rendering, the browser constructs the HTML for the ad,

    1. Download the seller rendering script (i.e. generateHTML()).
    2. Download the content of the buyer provided renderUrl.
    3. Run generateHTML(renderUrl, renderUrlResults) in an isolated worklet (no network connection and disk access).
    4. Inject the HTML into the Rendering Frame.

Further Design needed

Contextual Information Pass-through

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.

Instream Video Ads

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.

nllerandi3lift commented 8 months ago

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?

sunnypav commented 7 months ago

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.

timphsieh-google commented 7 months ago

Feedback

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:

Proposal: sellerRenderUrl

  1. During rendering, Chrome will load the contents of sellerRenderUrlinto 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).
  2. The rendering frame should load ad assets from the web bundle or from a pre-declared list of buyer provided URLs that were declared in the renderUrland sellerRenderUrl’s HTTP response header.

Proposal: Worklet running Seller Rendering Script

  1. After the on-device auction (but still within runAdAuction()), Chrome downloads buyer’s renderUrl.
  2. Chrome passes 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).
  3. Similar to above, during rendering, the rendering frame should load ad assets from the web bundle or from a pre-declared list of buyer provided URLs or seller provided URLs that were declared in the renderUrl’s HTTP response header.

Both proposals addressed the above concerns:

rdgordon-index commented 6 months ago

Curious how https://github.com/privacysandbox/privacy-sandbox-demos/pull/242 factors into this revised proposal.

timphsieh-google commented 6 months ago

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.