Skip to content

Commit 727bf20

Browse files
authored
Shared ID gdpr support (#6275)
* SharedId gdpr support * Reverted commented locally failing tests
1 parent 2640d08 commit 727bf20

File tree

6 files changed

+145
-10
lines changed

6 files changed

+145
-10
lines changed

modules/id5IdSystem.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,11 @@ export const id5IdSubmodule = {
166166
* It's permissible to return neither, one, or both fields.
167167
* @function extendId
168168
* @param {SubmoduleConfig} config
169+
* @param {ConsentData|undefined} consentData
169170
* @param {Object} cacheIdObj - existing id, if any
170171
* @return {(IdResponse|function(callback:function))} A response object that contains id and/or callback.
171172
*/
172-
extendId(config, cacheIdObj) {
173+
extendId(config, consentData, cacheIdObj) {
173174
const partnerId = (config && config.params && config.params.partner) || 0;
174175
incrementNb(partnerId);
175176
return cacheIdObj;

modules/pubCommonIdSystem.js

+16-4
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,17 @@ function handleResponse(pubcid, callback, config) {
136136
}
137137
}
138138

139+
/**
140+
* Builds and returns the shared Id URL with attached consent data if applicable
141+
* @param {Object} consentData
142+
* @return {string}
143+
*/
144+
function sharedIdUrl(consentData) {
145+
if (!consentData || typeof consentData.gdprApplies !== 'boolean' || !consentData.gdprApplies) return SHAREDID_URL;
146+
147+
return `${SHAREDID_URL}?gdpr=1&gdpr_consent=${consentData.consentString}`
148+
}
149+
139150
/**
140151
* Wraps pixelCallback in order to call sharedid sync
141152
* @param {string} pubcid Pubcommon id value
@@ -144,12 +155,12 @@ function handleResponse(pubcid, callback, config) {
144155
* @return {function(...[*]=)}
145156
*/
146157

147-
function getIdCallback(pubcid, pixelCallback, config) {
158+
function getIdCallback(pubcid, pixelCallback, config, consentData) {
148159
return function (callback) {
149160
if (typeof pixelCallback === 'function') {
150161
pixelCallback();
151162
}
152-
ajax(SHAREDID_URL, handleResponse(pubcid, callback, config), undefined, {method: 'GET', withCredentials: true});
163+
ajax(sharedIdUrl(consentData), handleResponse(pubcid, callback, config), undefined, {method: 'GET', withCredentials: true});
153164
}
154165
}
155166

@@ -227,7 +238,7 @@ export const pubCommonIdSubmodule = {
227238
}
228239

229240
const pixelCallback = this.makeCallback(pixelUrl, newId);
230-
const combinedCallback = enableSharedId ? getIdCallback(newId, pixelCallback, config) : pixelCallback;
241+
const combinedCallback = enableSharedId ? getIdCallback(newId, pixelCallback, config, consentData) : pixelCallback;
231242

232243
return {id: newId, callback: combinedCallback};
233244
},
@@ -247,10 +258,11 @@ export const pubCommonIdSubmodule = {
247258
*
248259
* @function
249260
* @param {SubmoduleParams} [config]
261+
* @param {ConsentData|undefined} consentData
250262
* @param {Object} storedId existing id
251263
* @returns {IdResponse|undefined}
252264
*/
253-
extendId: function(config = {}, storedId) {
265+
extendId: function(config = {}, consentData, storedId) {
254266
const {params: {extend = false, pixelUrl, enableSharedId = SHAREDID_DEFAULT_STATE} = {}} = config;
255267

256268
if (extend) {

modules/sharedIdSystem.js

+17-4
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,17 @@ function detectPrng(root) {
276276
return () => Math.random();
277277
}
278278

279+
/**
280+
* Builds and returns the shared Id URL with attached consent data if applicable
281+
* @param {Object} consentData
282+
* @return {string}
283+
*/
284+
function sharedIdUrl(consentData) {
285+
if (!consentData || typeof consentData.gdprApplies !== 'boolean' || !consentData.gdprApplies) return ID_SVC;
286+
287+
return `${ID_SVC}?gdpr=1&gdpr_consent=${consentData.consentString}`
288+
}
289+
279290
/** @type {Submodule} */
280291
export const sharedIdSubmodule = {
281292
/**
@@ -303,23 +314,25 @@ export const sharedIdSubmodule = {
303314
* performs action to obtain id and return a value.
304315
* @function
305316
* @param {SubmoduleConfig} [config]
317+
* @param {ConsentData|undefined} consentData
306318
* @returns {sharedId}
307319
*/
308-
getId(config) {
320+
getId(config, consentData) {
309321
const resp = function (callback) {
310322
utils.logInfo('SharedId: Sharedid doesnt exists, new cookie creation');
311-
ajax(ID_SVC, idGenerationCallback(callback), undefined, {method: 'GET', withCredentials: true});
323+
ajax(sharedIdUrl(consentData), idGenerationCallback(callback), undefined, {method: 'GET', withCredentials: true});
312324
};
313325
return {callback: resp};
314326
},
315327

316328
/**
317329
* performs actions even if the id exists and returns a value
318330
* @param config
331+
* @param consentData
319332
* @param storedId
320333
* @returns {{callback: *}}
321334
*/
322-
extendId(config, storedId) {
335+
extendId(config, consentData, storedId) {
323336
const configParams = (config && config.params) || {};
324337
utils.logInfo('SharedId: Existing shared id ' + storedId.id);
325338
const resp = function (callback) {
@@ -329,7 +342,7 @@ export const sharedIdSubmodule = {
329342
const sharedIdPayload = {};
330343
sharedIdPayload.sharedId = storedId.id;
331344
const payloadString = JSON.stringify(sharedIdPayload);
332-
ajax(ID_SVC, existingIdCallback(storedId, callback), payloadString, {method: 'POST', withCredentials: true});
345+
ajax(sharedIdUrl(consentData), existingIdCallback(storedId, callback), payloadString, {method: 'POST', withCredentials: true});
333346
}
334347
};
335348
return {callback: resp};

modules/userId/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
* It's permissible to return neither, one, or both fields.
2929
* @name Submodule#extendId
3030
* @param {SubmoduleConfig} config
31+
* @param {ConsentData|undefined} consentData
3132
* @param {Object} storedId - existing id, if any
3233
* @return {(IdResponse|function(callback:function))} A response object that contains id and/or callback.
3334
*/
@@ -621,7 +622,7 @@ function populateSubmoduleId(submodule, consentData, storedConsentData, forceRef
621622
response = submodule.submodule.getId(submodule.config, consentData, storedId);
622623
} else if (typeof submodule.submodule.extendId === 'function') {
623624
// If the id exists already, give submodule a chance to decide additional actions that need to be taken
624-
response = submodule.submodule.extendId(submodule.config, storedId);
625+
response = submodule.submodule.extendId(submodule.config, consentData, storedId);
625626
}
626627

627628
if (utils.isPlainObject(response)) {
+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import {
2+
sharedIdSubmodule,
3+
} from 'modules/sharedIdSystem.js';
4+
import { server } from 'test/mocks/xhr.js';
5+
6+
let expect = require('chai').expect;
7+
8+
describe('SharedId System', function() {
9+
const SHAREDID_RESPONSE = {sharedId: 'testsharedid'};
10+
11+
describe('Xhr Requests from getId()', function() {
12+
let callbackSpy = sinon.spy();
13+
14+
beforeEach(function() {
15+
callbackSpy.resetHistory();
16+
});
17+
18+
afterEach(function () {
19+
20+
});
21+
22+
it('should call shared id endpoint without consent data and handle a valid response', function () {
23+
let submoduleCallback = sharedIdSubmodule.getId(undefined, undefined).callback;
24+
submoduleCallback(callbackSpy);
25+
26+
let request = server.requests[0];
27+
expect(request.url).to.equal('https://id.sharedid.org/id');
28+
expect(request.withCredentials).to.be.true;
29+
30+
request.respond(200, {}, JSON.stringify(SHAREDID_RESPONSE));
31+
32+
expect(callbackSpy.calledOnce).to.be.true;
33+
expect(callbackSpy.lastCall.lastArg.id).to.equal(SHAREDID_RESPONSE.sharedId);
34+
});
35+
36+
it('should call shared id endpoint with consent data and handle a valid response', function () {
37+
let consentData = {
38+
gdprApplies: true,
39+
consentString: 'abc12345234',
40+
};
41+
42+
let submoduleCallback = sharedIdSubmodule.getId(undefined, consentData).callback;
43+
submoduleCallback(callbackSpy);
44+
45+
let request = server.requests[0];
46+
expect(request.url).to.equal('https://id.sharedid.org/id?gdpr=1&gdpr_consent=abc12345234');
47+
expect(request.withCredentials).to.be.true;
48+
49+
request.respond(200, {}, JSON.stringify(SHAREDID_RESPONSE));
50+
51+
expect(callbackSpy.calledOnce).to.be.true;
52+
expect(callbackSpy.lastCall.lastArg.id).to.equal(SHAREDID_RESPONSE.sharedId);
53+
});
54+
});
55+
});

test/spec/modules/userId_spec.js

+53
Original file line numberDiff line numberDiff line change
@@ -2037,6 +2037,8 @@ describe('User ID', function () {
20372037
coreStorage.setCookie('pubcid_sharedid', '', EXPIRED_COOKIE_DATE);
20382038
coreStorage.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE);
20392039
coreStorage.setCookie('_parrable_eid', '', EXPIRED_COOKIE_DATE);
2040+
resetConsentData();
2041+
delete window.__tcfapi;
20402042
});
20412043

20422044
it('pubcid callback with url', function () {
@@ -2171,6 +2173,57 @@ describe('User ID', function () {
21712173
expect(server.requests[0].url).to.equal('https://id.sharedid.org/id');
21722174
expect(coreStorage.getCookie('pubcid_sharedid')).to.be.null;
21732175
});
2176+
2177+
it('verify sharedid called with consent data when gdpr applies', function () {
2178+
let adUnits = [getAdUnitMock()];
2179+
let customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']);
2180+
let consentConfig = {
2181+
cmpApi: 'iab',
2182+
timeout: 7500,
2183+
allowAuctionWithoutConsent: false
2184+
};
2185+
customCfg = addConfig(customCfg, 'params', {pixelUrl: '/any/pubcid/url', enableSharedId: true});
2186+
2187+
server.respondWith('https://id.sharedid.org/id?gdpr=1&gdpr_consent=abc12345234', function(xhr) {
2188+
xhr.respond(200, {}, '{"sharedId":"testsharedid"}');
2189+
});
2190+
server.respondImmediately = true;
2191+
2192+
let testConsentData = {
2193+
tcString: 'abc12345234',
2194+
gdprApplies: true,
2195+
purposeOneTreatment: false,
2196+
eventStatus: 'tcloaded',
2197+
vendor: {consents: {887: true}},
2198+
purpose: {
2199+
consents: {
2200+
1: true
2201+
}
2202+
}
2203+
};
2204+
2205+
window.__tcfapi = function () { };
2206+
sinon.stub(window, '__tcfapi').callsFake((...args) => {
2207+
args[2](testConsentData, true);
2208+
});
2209+
2210+
setSubmoduleRegistry([pubCommonIdSubmodule]);
2211+
init(config);
2212+
config.setConfig(customCfg);
2213+
setConsentConfig(consentConfig);
2214+
2215+
consentManagementRequestBidsHook(() => {
2216+
}, {});
2217+
requestBidsHook((config) => {
2218+
}, {adUnits});
2219+
2220+
expect(utils.triggerPixel.called).to.be.false;
2221+
events.emit(CONSTANTS.EVENTS.AUCTION_END, {});
2222+
expect(utils.triggerPixel.getCall(0).args[0]).to.include('/any/pubcid/url');
2223+
2224+
expect(server.requests[0].url).to.equal('https://id.sharedid.org/id?gdpr=1&gdpr_consent=abc12345234');
2225+
expect(coreStorage.getCookie('pubcid_sharedid')).to.equal('testsharedid');
2226+
});
21742227
});
21752228

21762229
describe('Set cookie behavior', function () {

0 commit comments

Comments
 (0)