tab-cmp
v0.15.0-alpha.1
Published
A consent management platform wrapper for Tab for a Cause
Downloads
54
Readme
tab-cmp
A Consent Management Platform (CMP) for Tab for a Cause. Creates IAB-compliant window.__tcfapi
and window.__uspapi
functions and saves users' data privacy options.
Get Started
- Add the
tagModified.html
code to the<head />
of the page. yarn add tab-cmp
- On any page that should have a functional CMP (likely the entire app), intialize the CMP:
import tabCMP from 'tab-cmp'
tabCMP.initializeCMP({
// Set configuration as needed.
displayPersistentConsentLink: false,
onError: (err) => {
console.error(err)
},
primaryButtonColor: '#9d4ba3',
publisherName: 'Tab for a Cause',
publisherLogo: tabLogoWithText,
})
- Optional: Ignore unhelpful unhandled rejections from Quantcast Choice. In your app's error logging logic, ignore:
- Errors with name
GVLError
. This is a network failure when fetching the vendor list. - Errors with message
Failed to read the 'localStorage' property from 'Window': Access is denied for this document.
. This is an expected error for the new tab page cross-domain frame in some browsers.
- Errors with name
Why This Package Exists
There are two reasons we aren't simply relying on another vanilla, third-party CMP:
- iframe support: Our app often loads within a cross-domain iframe in the new tab page extension. We need the CMP to work normally in the iframe as it would on a top frame.
- Speed: Third-party CMPs can take 100s of milliseconds to load and respond, which delays ad requests. The nature of our app requires the CMP to be faster.
If a third-party CMP can support these needs, we can consider deprecating this module.
API
async initializeCMP(config)
Required before calling other methods. Determines the user's country location, runs the QC Choice JS, and sets default USP data as needed.
async doesGDPRApply()
Resolves to true if the user is in the EU. Use this to know whether to render GDPR-related privacy options.
async doesCCPAApply()
Resolves to true if the user is in the US. Use this to know whether to render CCPA-related privacy options.
async openTCFConsentDialog()
Opens the GDPR consent dialog.
async openCCPAConsentDialog()
Opens the CCPA data privacy dialog.
Developing & Updating
What We Changed
This module wraps the Quantcast Choice CMP. We modify its behavior in a few ways:
- We change the privacy data cookies to
SameSite=None
(fromSameSite=Lax
) so the CMP works in a cross-domain iframe. We package a static version of QC Choice's CMP JS file with the necessary changes. - We handle geolocation ourselves. This is necessary because QC Choice's CMP JS file is normally dynamic based on location, and we use static JS.
- We add functional stubs for the TCF and USP APIs that relies on data we sync to local storage. This allows our CMP to respond rapidly before QC Choice JS has initialized.
- We add debugging and error logging.
External Links/Documentation
- Quantcast Choice
- Ad partners
- Prebid GDPR and US privacy docs
- Amazon privacy docs
- Index Exchange privacy docs
How to Modify Settings or Update to a New Version
Upgrading or modifying Quantcast Choice settings requires a combination of modifying the settings in the QC Choice management panel and modifying local code. Note that some of our CMP settings use the QC Choice panel settings directly, but many settings are hardcoded locally.
- Update the
tab.gladly.dev
settings in the QC Choice management panel - Update the version in the URL of the NPM script
quantcast:cmp-js:download
to match the portal version - Run
yarn run quantcast:update
- Manually reconcile changes in
./quantcast/qcChoiceOriginal.js
to update./src/qcChoiceModified.js
and./src/initCMP.js
- If
./quantcast/qcCmpOriginal.js
has changed:- Ensure it still uses the cookie and geolocation logic that we expect to modify (see the
quantcast:cmp-js:copy-with-edits
NPM script) - Run
yarn run quantcast:cmp-js:copy-with-edits
- Ensure it still uses the cookie and geolocation logic that we expect to modify (see the
- Manually test the CMP in a staging environment. See the test checklist below.
- Update
tab.gladly.io
settings in the QC Choice management panel to match thetab.gladly.dev
settings
Debugging
- Add a
tabCMPDebug=true
URL parameter to the page to enable debug logging - For
tab-ads
, which is useful to test ad partner behavior: add atabAdsDebug=true
URL parameter to the page to enable debug logging - For Google Ad Manager: enable the Google Publisher Console
Preparing to Test the Integration
We'll want to test
- basic CMP functionality
- the CMP UI consent dialogs
- the integration with ad partners
for each combination of
- privacy context (GDPR/EU or CCPA/US)
- frame context (as a top-level frame or in the new tab page iframe).
This section has info on how to set up these test contexts.
Clearing Existing Data
To reset CMP state:
- delete
tabCMP*
local storage values - delete the following coookies:
euconsent-v2
,usprivacy
,addtl_consent
- Clear cache storage after changing VPN location to ensure the geolocation is updated for third party scripts (e.g. Google Ad Manager or Amazon)
How to Set Geolocation
EU/GDPR
- In local storage, ensure
tabCMP.clientLocation.isInEU
has a value oftrue
andtabCMP.clientLocation.countryISOCode
has a value ofFR
(or some other EU country ISO code). - Enable a VPN into that country so our ad partners behave as they will with EU traffic. The Outline app with AWS Lightsail serves as a functional VPN.
US/CCPA
- In local storage, ensure
tabCMP.clientLocation.isInEU
has a value offalse
andtabCMP.clientLocation.countryISOCode
has a value ofUS
. - If you're not in the US, enable a VPN into the US.
How to Set Frame Context
Our app often loads in the new tab page iframe, in the context of a browser extension. CMP and ad partner behavior can be different in an iframe, so we have to specifically test it.
App as Top Frame
- Visit Tab for a Cause directly and test there.
Within an Iframe
- Open a new tab page with the Tab for a Cause browser extension enabled.
- If testing non-production versions of the app, manually modify the iframe
src
to use that domain. To refresh, modify thesrc
rather than refreshing the top-level page. - When testing CMP APIs, do so from the app frame, not the top-level extension frame.
Test Checklist
US/CCPA
Basic CMP functionality
A new user has the expected default privacy string. Clear the CMP data, then run:
__uspapi('getUSPData', 1, (uspData, success) => { console.log('cmp responded:', uspData, success) })
The
uspString
property value should be1YNN
.Works on:
- [ ] top frame
- [ ] iframed new tab
GDPR should not apply. Run:
__tcfapi('getTCData', 2, (tcData, success) => { console.log('cmp responded:', tcData, success) })
The
gdprApplies
property value should befalse
. Works on:- [ ] top frame
- [ ] iframed new tab
CMP Consent Dialog
The user's account page should show a "Do Not Show My Info" link that opens the CCPA dialog.
Works on:
- [ ] top frame
- [ ] iframed new tab
The user's choice should persist. Open the dialog, opt out of data sale, and save. Refresh the app, open the dialog, and confirm you are still opted out.
Works on:
- [ ] top frame
- [ ] iframed new tab
After opting out of data sale, the USP string changes. Run:
__uspapi('getUSPData', 1, (uspData, success) => { console.log('cmp responded:', uspData, success) })
The
uspString
property value should be1YYN
.Works on:
- [ ] top frame
- [ ] iframed new tab
Ad Partner Behavior
The request to Google Ad Manager includes the expected privacy options. For the request to
securepubads.g.doubleclick.net/gampad/
: theus_privacy
query string value is1YNN
and thegdpr
value is0
.Works on:
- [ ] top frame
- [ ] iframed new tab
The request to Amazon has expected privacy options. For the request to
https://c.amazon-adsystem.com/e/dtb/bid
: thepj
query string value is{"us_privacy":"1YNN"}
, thegdpre
value is0
, and thegdprl.status
value istcfv2-success
.Works on:
- [ ] top frame
- [ ] iframed new tab
A Prebid partner uses expected privacy options. E.g., for the Sonobi request to
apex.go.sonobi.com/trinity.json
: theus_privacy
query string value is1YNN
and thegdpr
value isfalse
.Works on:
- [ ] top frame
- [ ] iframed new tab
Index Exchange uses expected privacy options. For the request to
htlb.casalemedia.com/cygnus
: ther
query string value includes theregs
property equal to{"ext":{"gdpr":0,"us_privacy":"1YNN"}}
.Works on:
- [ ] top frame
- [ ] iframed new tab
The request to fetch ads is not substantially slower than prior to making CMP changes. Compare the request time of
securepubads.g.doubleclick.net/gampad/
to a prior deployment.Confirmed on:
- [ ] top frame
It's challenging to accurately test timing in the iframe, so no need to.
EU/GDPR
Basic CMP functionality Start by clearing the CMP data, then consenting to data usage.
The CMP responds with expected data. Run:
__tcfapi('getTCData', 2, (tcData, success) => { console.log('cmp responded:', tcData, success) })
The
gdprApplies
property value should betrue
, and thetcString
andaddtlConsent
properties should be set.Works on:
- [ ] top frame
- [ ] iframed new tab
CCPA should not apply. Run:
__uspapi('getUSPData', 1, (uspData, success) => { console.log('cmp responded:', uspData, success) })
The
uspString
property value should be1---
. Works on:- [ ] top frame
- [ ] iframed new tab
CMP Consent Dialog
The consent dialog appears on first use.
- Clear the CMP data, then refresh the page.
- Ensure the dialog appears.
- Choose some custom data use consents and save.
- Refresh the page and ensure the dialog does not reappear.
Works on:
- [ ] top frame
- [ ] iframed new tab
The user's account page should show a "Privacy Options" button that opens the GDPR dialog. Opening it should show the options you previously selected.
Works on:
- [ ] top frame
- [ ] iframed new tab
The user's choices should persist. Open the dialog, change your options, and save. Refresh the app, open the dialog, and confirm your changes are still there.
Works on:
- [ ] top frame
- [ ] iframed new tab
Ad Partner Behavior
The request to Google Ad Manager includes the expected privacy options. For the request to
securepubads.g.doubleclick.net/gampad/
: theus_privacy
query string value is1---
, thegdpr
value is1
, thegdpr_consent
value is the consent string (matching theeuconsent-v2
cookie value), andaddtl_consent
is a string of numbers.Works on:
- [ ] top frame
- [ ] iframed new tab
The request to Amazon has expected privacy options. For the request to
https://c.amazon-adsystem.com/e/dtb/bid
: thepj
query string value is{"us_privacy":"1---"}
, thegdpre
value is1
, thegdprc
value is the consent string, and thegdprl.status
value istcfv2-success
.Works on:
- [ ] top frame
- [ ] iframed new tab
A Prebid partner uses expected privacy options. E.g., for the Sonobi request to
apex.go.sonobi.com/trinity.json
: theus_privacy
query string value is1---
, thegdpr
value istrue
, and theconsent_string
value is the consent string.Works on:
- [ ] top frame
- [ ] iframed new tab
Index Exchange uses expected privacy options. For the request to
htlb.casalemedia.com/cygnus
: ther
query string value includes theregs
property equal to{"ext":{"gdpr":1,"us_privacy":"1---"}}
and anext
property with aconsent
value equal to the consent string.Works on:
- [ ] top frame
- [ ] iframed new tab
The request to fetch ads is not substantially slower than prior to making CMP changes. Compare the request time of
securepubads.g.doubleclick.net/gampad/
to a prior deployment.Confirmed on:
- [ ] top frame
It's challenging to accurately test timing in the iframe, so no need to.