Starting with version 7.52, Prebid.js introduced a centralized control mechanism for privacy-sensitive activities - such as accessing device storage or sharing data with partners. These controls are intended to serve as building blocks for privacy protection mechanisms, allowing module developers or publishers to directly specify what should be permitted or avoided in any given regulatory environment.
There are many privacy regulations that Prebid publishers need to accommodate. Prebid supplies modules to help Publishers implement their legal policies, but there are scenarios where extra control is needed:
Several, but not all, of the popular consent strings have modules (eg Prebid Activity Controls – GPP control module - usnat) that translate their contents into activity controls. These modules may have some overrides to default string interpretations available. When these overrides are insufficient for a publisher, or case law has abruptly changed, publishers may prefer direct control.
We did an analysis of the things Prebid.js does and identified those related to privacy regulations. We call these things “potentially restricted activities”, or just “activities” for short. Here are some:
The full list of activities Prebid.js supports is below.
Think of an activity control as a ‘gatekeeper’ that makes the decision about whether the activity should be allowed in this specific context:
Prebid.js core checks with the Activity Controls to see whether an activity is allowed. The configuration for the activity can come from modules, custom functions in the page, or a rule-based JSON config.
In this example, bidderX wants to set a cookie through StorageManager, which queries the Activity Control System to determine whether that’s allowed. The publisher has set up configuration that specifically enables bidderX to do this.
Here’s an example JSON config that disables accessing local storage (including cookies) for everything except the bid adapter bidderX
:
pbjs.setConfig({
allowActivities: {
accessDevice: {
default: false,
rules: [{
condition(params) {
return params.componentName === 'bidderX'
},
allow: true
}]
}
}
})
allowActivities
is a new option to setConfig. It contains a list of activity names – see the full list of activities below. Each activity is an object that can contain these attributes:
Name | Type | Description |
---|---|---|
default |
Boolean | Whether the activity should be allowed if no other rule applies. Defaults to true. |
rules |
Array of objects | Rules for this activity |
rules[].condition |
Function | Condition function to use for this rule; the rule applies only if this returns true. Receives a single object that contains activity parameters as input. If omitted, the rule always applies. |
rules[].allow |
Boolean | Whether the activity should be allowed when this rule applies. Defaults to true. |
rules[].priority |
Number | Priority of this rule compared to other rules; a lower number means higher priority. See note on rule priority below. Defaults to 1. |
Rules
is an array of objects that a publisher can construct to provide fine-grained control over a given activity. For instance, you could set up a series of rules that says:
There’s more about rules below.
Here’s the list of the ‘potentially restricted activities’ that Prebid.js core can restrict for Publishers:
Name | Description | Effect when denied | Additional parameters |
---|---|---|---|
accessDevice |
A component wants to use device storage | Storage is disabled | storageType |
enrichEids |
A user ID or RTD submodule wants to add user IDs to outgoing requests | User IDs are discarded | None |
enrichUfpd |
A Real-Time Data (RTD) submodule wants to add user first-party data to outgoing requests (user.data in ORTB) |
User FPD is discarded | None |
fetchBids |
A bid adapter wants to participate in an auction | Bidder is removed from the auction | configName |
reportAnalytics |
An analytics adapter is being enabled through pbjs.enableAnalytics |
Adapter remains disabled | None |
syncUser |
A bid adapter wants to fetch a user sync | User sync is skipped | syncType , syncUrl |
transmitEids |
A bid adapter or RTD submodule wants to access and/or transmit user IDs to their endpoint | User IDs are hidden from the component | configName |
transmitPreciseGeo |
A bid adapter or RTD submodule wants to access and/or transmit precise geolocation data to their endpoint | Component is allowed only 2-digit precision for latitude and longitude | configName |
transmitTid |
A bid adapter or RTD submodule wants to access and/or transmit globally unique transaction IDs to their endpoint | Transaction IDs are hidden from the component | configName |
transmitUfpd |
A bid adapter or RTD submodule wants to access and/or transmit user FPD to their endpoint | User FPD is hidden from the component | configName |
loadExternalScript |
A bid adapter, analytics adapter, or module wants to load external script | Module is not allowed to load external script from adloader.js |
None |
There are three parts to an Activity Control’s rule:
For example, this rule would allow bidderX to perform the activity if no higher priority rules take precedence.
...
rules: [{
priority: 10, // average priority
condition(params) {
return params.componentName === 'bidderX'
},
allow: true
}]
...
Activity control rules in Prebid.js can be created by two main sources:
setConfig({allowActivities})
as in the examples shown here. When set this way, rules are considered the highest priority value of 1.When rules are processed, they are sorted by priority, and all rules of the same priority are considered to happen at the same time. The details:
allow: false
, the activity is DENIED.allow: true
, the activity is ALLOWED.default: false
, the activity is DENIED.So this means that when priority
is omitted from allowActivities
configuration, it acts as an override over other control mechanisms. For example:
pbjs.setConfig({
accessDevice: false, // this would have the effect of disabling device storage, but...
allowActivities: {
accessDevice: {
rules: [
{allow: true} // ... it's overridden by this condition-less rule with a default priority of 1
]
}
}
})
If a priority number greater than 10 is specified, the rule only takes effect when all other controls have allowed the activity. For example:
pbjs.setConfig({
allowActivities: {
accessDevice: {
// the intent here is to disable cookies but allow HTML5 localStorage
// because this defines priority > 10, other controls will be checked first
// e.g. if GDPR is in-scope and there's no consent, this priority 20 rule won't be processed
default: false,
rules: [{
condition({storageType}) {
return storageType === 'html5'
},
allow: true,
priority: 20
}]
}
}
})
A condition
is a javascript function that receives information about the activity that is about to be performed. If a condition evaluates to true, the allow
attribute of the rule will be utilized. If there’s no condition specified, the rule’s allow
attribute will always be utilized.
These are the parameters available to the condition function:
Name | Type | Available for | Description |
---|---|---|---|
componentType |
String | All activities | One of: 'bidder' , 'userId' , 'rtd' , 'analytics' , or 'prebid' ; identifies the type of component (usually a module) that wishes to perform the activity. 'prebid' is reserved for Prebid core itself and a few “privileged” modules such as the PBS adapter. |
componentName |
String | All activities | Name of the component; this is (depending on the type) either a bidder code, user ID or RTD submodule name, analytics provider code, or module name. |
component |
String | All activities | This is always a dot-separated concatenation of componentType and componentName ; for example, with {componentType: 'bidder', componentName: 'bidderX'} , component is 'bidder.bidderX' . |
adapterCode |
String | All activities | If componentType is 'bidder' , and componentName is an alias, then adapterCode is the bidder code that was aliased; or identical to componentName if the bidder is not an alias. This is undefined when the component is not a bidder. |
configName |
String | fetchBids |
When the Prebid Server adapter is part of an auction, this is the name given to its s2s configuration, if any. |
storageType |
String | accessDevice |
Either 'html5' or 'cookie' - the device storage mechanism being accessed. |
syncType |
String | syncUser |
Either 'iframe' or 'image' - the type of user sync. |
syncUrl |
String | syncUser |
URL of the user sync. |
If the rule’s condition matches, this attribute defines whether the rule ‘votes’ to allow (true) or disallow (false) the activity in question.
As noted in the priority section, disallow (false) takes precedence amongst rules at the same priority level.
If allow
is not defined, the rule is assumed to assert true (i.e. allow the activity to take place).
This is similiar to the ‘vendor exception’ feature of the TCF Control Module. This would always allow bidderA to participate in the auction, even without explicit consent in GDPR scenarios. It might indicate, for instance, that this is a ‘first party bidder’.
pbjs.setConfig({
allowActivities: {
fetchBids: {
rules: [{
condition: ({componentName}) => componentName === 'bidderA',
allow: true
}]
}
}
})
const DOMAINLIST = [
'https://example-domain.org',
'https://other-domain.com',
];
pbjs.setConfig({
allowActivities: {
syncUser: {
default: false,
rules: [{
condition({syncUrl}) {
return DOMAINLIST.some(domain => syncUrl.startsWith(domain));
},
allow: true
}]
}
}
})
pbjs.setConfig({
allowActivities: {
transmitEids: {
rules: [{
condition: ({componentName}) => componentName === 'exampleVendor',
allow: false,
}]
}
}
})
Reference: US Privacy User Signal Mechanism “USP API” Specification
function isCCPAConsentDenied() {
### assumes uspapi is properly implemented and available in your environment.
### check usp string for the second character (notice) is not 'Y' or third character (opt out) is not 'N' or first character (version) is not '1'
__uspapi('getUSPData', 1 , (uspData, success) => { if(uspData.uspString.charAt(2) ==='Y' || uspData.uspString.charAt(1) !=='Y'|| uspData.uspString.charAt(0) !=='1') { return true } else { return false }});
}
pbjs.setConfig({
allowActivities: {
enrichUfpd: {
rules: [{
condition: isCCPAConsentDenied,
allow: false
}]
},
enrichEids: {
rules: [{
condition: isCCPAConsentDenied,
allow: false
}]
},
reportAnalytics: {
rules: [{
condition: isCCPAConsentDenied,
allow: false
}]
},
syncUser: {
rules: [{
condition: isCCPAConsentDenied,
allow: false
}]
},
transmitEids: {
rules: [{
condition: isCCPAConsentDenied,
allow: false
}]
},
transmitPreciseGeo: {
rules: [{
condition: isCCPAConsentDenied,
allow: false
}]
},
transmitUfpd: {
rules: [{
condition: isCCPAConsentDenied,
allow: false
}]
}
}
})
This example might be useful for publishers using a version of Prebid.js that supports activity controls but does not support the USNat module.
if (in-page code to detect that GPP SID 7 through 12 are in-scope or if the GPC flag is set) {
pbjs.setConfig({
allowActivities: {
enrichEids: {
default: false
},
transmitEids: {
default: false
},
… see other activities in https://docs.prebid.org/dev-docs/activity-controls.html …
}
});
}
To make exceptions for certain IDs, there are two steps:
This approach works in conjunction with other activity-control compiant modules (like the GPP USNat module.
pbjs.setConfig({
allowActivities: {
enrichEids: {
default: false,
priority: 1,
rules: [{
condition(params) {
return params.componentName === 'sharedIdSystem'
},
allow: true
}]
},
transmitEids: {
rules: [{
allow: true
},
… see other activities in https://docs.prebid.org/dev-docs/activity-controls.html …
}
});