Skip to content
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

CCPA (US Privacy) #4425

Closed
harpere opened this issue Nov 6, 2019 · 10 comments
Closed

CCPA (US Privacy) #4425

harpere opened this issue Nov 6, 2019 · 10 comments

Comments

@harpere
Copy link
Collaborator

harpere commented Nov 6, 2019

Type of issue

Feature

Description

The objective is to provide a mechanism within PBJS that follows the IAB specification for complying with the CCPA law that goes into affect Jan 2, 2020. The requirements and general approach are similar to that of GDPR, but there are some key differences. Note that the IAB specification defines standards for a general API they call USP (US Privacy) to hopefully support future US Privacy laws in addition to the current CCPA law. For more information see https://iabtechlab.com/standards/ccpa/

This does not address TCF 2.0.

USP (CCPA) IAB Spec is not yet Complete

  • the IAB CCPA tech spec public comment period ended Nov 5, so at the time of this writing, the specs are not yet final.
  • the postMessage interface for cross-domain iframes has not yet been defined. At this time the spec says, "the implementation must provide a proxy for postMessage events targeted to the
    __uspapi interface sent from within nested iframes. GUIDANCE TO COME on iframes for
    working with IAB SafeFrames."

General Approach:

  1. Generalize the existing Prebid.js GDPR Consent Management module to support both GDPR and CCPA/USPrivacy.
  2. robustly gather the USP signal (we do not need to parse it)
  3. make the USP signal available to each bidder
  4. It's the bidder adapter's responsibility to send the signal to their endpoint
  5. It's the exchange/DSP responsibility to enforce the USP rules as determined by their legal teams

Differences with GDPR

  • the USP (CCPA) api function __uspapi() always responds synchronously, whether or not privacy data is available, while the GDPR CMP may respond asynchronously, especially if waiting for a user response. Unfortunately this doesn't necessarily mean it's always a synchronous transaction because if there are nested cross-domain iframes we may need to do an async postMessage().
  • GDPR is "opt-out" (tracking data is assumed to be restricted unless the user opts-out), while CCPA is "opt-in" (tracking data is not restricted unless the user opts-in).

Key Assumptions

  • Because CCPA is "opt-in", we don't need to worry about an "allow_auction_without_consent" preference, when consent data could not be obtained, like we do with GDPR.
  • Because the USP API does not wait for a user response, if it was not successfully obtained before the first auction, we should try again to retrieve privacy data before each subsequent auction.

Implementation Notes and Recommendations

  • The IAB Spec says, "__uspapi() must always be a function at all times, even at initialization – the API must be able to handle calls at all times."
  • Because for performance reasons we want GDPR and USP (CCPA) to be handled at the same time (because a USP API postMessage() would be asynchronous), I'm recommending that USP support be added directly to the existing consentManagement module. A more elegant solution might be putting this in a sub-module that's managed by the consentManagement module (and eventually moving the GDPR support to a submodule as well), but I think this would be more complicated. Another approach might be making use of a flexible Real-Time-Data Module, but we don't have that yet. So in the interest of time, considering we have a tight deadline, I'm proposing just putting it in the existing consentManagement module. But, I'm open to other opinions for sure.
  • By default the consentManagement module will look for GDPR consent data as it does now. To support USP (CCPA) privacy data, we'll add a new consentAPIs config param to indicate which consent/privacy mechanisms to run. The param will be an array with acceptable values gdpr and usp.
  • When an async USP postMessage() is used, a default timeout of 50ms will be used (this is not relevant for the synchronous __uspapi() call). The publisher can override this with the uspTimeout param. For example this config would run both GDPR and USP consent/privacy mechanisms with a 3000ms GDPR timeout and 100ms USP (CCPA) timeout:
pbjs.setConfig({
       consentManagement: {
          gdpr: {
            cmpApi: 'iab',
            timeout: 3000,
            allowAuctionWithoutConsent: false
         },
         usp: {
            cmpApi: 'iab',
            timeout: 100 // (default is 50ms)
         }
       }
});

Handling Timeouts

When handling both GDPR and an async USP postMessage, the idea is to give each request the maximum amount of time to respond without needlessly delaying the bid request. So, each request should be given at least as much time as their timeout setting, but should be allowed to continue until the other request has returned or timed out, whichever is first. For example, if the GDPR timeout is 500ms and the uspTimeout is 50ms, the USP postMessage() should be given at least 50ms, but if after 50ms it has not finished, it should be allowed to run until the GDPR request finishes or times out (500ms), whichever comes first.

When handling GDPR and the synchronous __uspapi() call, we should make the __uspapi() request after the GDPR response (or timeout), giving as much time as possible for the underlying USP system to gather its privacy data. Because it's a synchronous call it will not delay the bid request.

us_privacy Parameter

us_privacy is a string parameter that will be used to pass US based consumer privacy preferences to interested parties (DMPs, SSPs, DSPs (bidders), etc). This is passed as part of the uspData object (uspData.uspString) sent to the callback in the __uspapi() example below, and presumably the returnValue in the assumed postMessage() example below. Currently this parameter only contains CCPA data, but could be used for other US based privacy data in the future. PBJS's only concern is passing this parameter in GET or RTB requests (see below). We do not have to parse the string or worry about its content. If interested, more information on this string can be found in this IAB spec.

GET Requests

To pass the privacy string as a url parameter in a GET request, a uri encoded querystring parameter named us_privacy will be used. This is also the parameter that will be added to the bidderRequest object (bidderRequest.us_privacy) to be used by the bid adapters. (question - should we encode it before adding to the bidderRequest? What do we do for GDPR?)

RTB Requests

As explained in this IAB RTB spec, the us_privacy string should be passed in RTB requests in an extension object within the BidRequest object. For OpenRTB v2.2+, the “us_privacy” attribute should be added into the “ext” object within the “regs” object. This is the format that the Prebid Server Adapter should use for PBS. For example:

{  
    "regs": {
        "ext": {
            "us_privacy": "1YNY"
        }
    }
}

Calling the __uspapi() Interface Function

As described in this iab spec, similar to the __cmp('getConsentData', null, callback); interface function used to acquire the GDPR privacy data, with USP (CCPA) there is a __uspapi(command, version, callback) interface function that should be called as follows:

__uspapi('getUSPData', 1, (uspData, success) => {
    if (success) { have uspData, add to request; }
    else { don't have uspData, don't add to request }
});

Finding the __uspapi() function

NOTE: if there is indeed support for a locator as described in the x-domain iframe section below (like GDPR), we may be able to use that approach instead.

First look for the __uspapi() function in the current window, and in case of friendly iframes, in an ancestor window. For example:

let w = window;
let uspRef = w.__uspapi;
while (!uspRef && w !== window.top) {
    w = w.parent;
    try {
        uspRef = w.__uspapi;
    } catch (e) {}
}

cross-domain Iframes

When PBJS is loaded into a cross-domain iframe it will likely not have direct access to the __uspapi() function (and will not be found by the example above). As mentioned above, the IAB spec has not yet documented how to handle this scenario, but for the moment we'll assume it'll work similarly to the GDPR CMP __cmpLocator and postMessage() interface, something like:

// find the USP API window
let w = window;
let uspWin;
while (!uspWin && w !== window.top) {
    w = w.parent;
    try {
        if (w.frames["__uspapiLocator"]) uspWin = w;
    } catch(e) {}
}
if (!uspWin) {
    return; // no USP API window found, so bail
}
 
// found USP API Window  
var callId = 'usp-' + Math.random();
uspWin.postMessage({
    __uspCall: {
        callId: callId,
        command: 'getUSPData',
        version: 1
    }
}, '*');
 
window.addEventListener('message', function (event) {
    try {
        var ret = (typeof event.data === 'string' ? JSON.parse(event.data) : event.data).__uspReturn;
        if (ret && ret.callId === callId) {
            processUspData(ret.returnValue, ret.success);  // similar to __uspapi() callback above
        }
    } catch (e) {} // do nothing
});

Prebid Server Bid Adapter

To support CCPA, Prebid Server needs two things from Prebid.js:

  1. Add regs.ext.us_privacy to each PBS request
  2. If the us_privacy value exists, pass it through the /cookie_sync POST body as "us_privacy".
@pm-harshad-mane
Copy link
Contributor

Thank you @harpere for sharing notes 👍

@pm-harshad-mane
Copy link
Contributor

I agree with the idea of dividing GDPR and USP into sub-modules.

@bretg bretg changed the title CCPA (US Privacy) Intent to Implement CCPA (US Privacy) Nov 6, 2019
@bretg
Copy link
Collaborator

bretg commented Nov 6, 2019

@pm-harshad-mane - we want to build out sub-modules at some point, but given the tight timeline, we're proposing to combine them for now into the ConsentManagement module. We can split them out in 1Q20.

FYI - I added a section on supporting Prebid Server.

@dmdabbs
Copy link

dmdabbs commented Nov 22, 2019

Hello. Now that IAB have released the specs, https://iabtechlab.com/standards/ccpa/, what is the status of USPrivacy support? I would like to be able to set our engineers to updating our Prebid-based adaptor to read the usp string and pass up to our server.

@bretg
Copy link
Collaborator

bretg commented Nov 23, 2019

The pull request is in progress. Should be available Monday or Tues for review.

@bretg
Copy link
Collaborator

bretg commented Nov 26, 2019

@harpere - I think we're missing an important feature here -- usersync support.

See the details for GDPR at http://prebid.org/dev-docs/modules/consentManagement.html#usersync-integration

@PWyrembak
Copy link
Contributor

Hi there,
trustx prebid adapter is willing to support the CCPA “us_privacy” string from the first release days. To be 100% sure of a proper param obtaining could you please confirm that this piece of code would work correctly:

buildRequests: function(validBidRequests, bidderRequest) {
...
    if (bidderRequest.us_privacy) {
        payload.us_privacy = bidderRequest.us_privacy;
    }
...
}

@harpere
Copy link
Collaborator Author

harpere commented Dec 3, 2019

@PWyrembak the way it's implemented, it will be bidderRequest.uspConsent. So your example would be:

buildRequests: function(validBidRequests, bidderRequest) {
...
    if (bidderRequest.uspConsent) {
        payload.us_privacy = bidderRequest.uspConsent;
    }
...
}

@bretg
Copy link
Collaborator

bretg commented Dec 3, 2019

Updated issue description with the config that came out of the development process. Note that there is backwards compatibility with previous GDPR consent management, though the documentation will only show the new convention.

@bretg
Copy link
Collaborator

bretg commented Dec 9, 2019

This feature was released with 2.43. Closing the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants