Skip to content

Commit

Permalink
Uid2 and friends: Cleanup issues around optout in UID2 and EUID (#11505)
Browse files Browse the repository at this point in the history
* handle optout in UID2 decodeImpl()

* better log message when CSTG params are not set

* better log message when CSTG params are not set
  • Loading branch information
ssundahlTTD authored May 30, 2024
1 parent ec7b909 commit 1e88c2f
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 3 deletions.
4 changes: 2 additions & 2 deletions integrationExamples/gpt/cstg_example.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
function updateUID2GuiElements() {
console.log('Updating displayed values.');
const uid2 = pbjs.getUserIds().uid2;
document.querySelector('#uid2TargetedAdvertisingReady').innerText = uid2 ? 'yes' : 'no';
document.querySelector('#uid2AdvertisingToken').innerText = uid2 ? String(uid2.id) : '';
document.querySelector('#uid2TargetedAdvertisingReady').innerText = uid2 && !uid2.optout ? 'yes' : 'no';
document.querySelector('#uid2AdvertisingToken').innerText = uid2 && !uid2.optout ? String(uid2.id) : uid2 && uid2.optout ? 'Optout' : '';
if (!uid2) {
document.querySelector('#uid2LoginForm').style['display'] = 'block';
document.querySelector('#uid2ClearStorageForm').style['display'] = 'none';;
Expand Down
4 changes: 4 additions & 0 deletions modules/uid2IdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ function decodeImpl(value) {
const result = { uid2: { id: value } };
return result;
}
if (value.latestToken === 'optout') {
_logInfo('Found optout token. Refresh is unavailable for this token.');
return { uid2: { optout: true } };
}
if (Date.now() < value.latestToken.identity_expires) {
return { uid2: { id: value.latestToken.advertising_token } };
}
Expand Down
6 changes: 5 additions & 1 deletion modules/uid2IdSystem_shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,15 @@ if (FEATURES.UID2_CSTG) {
clientSideTokenGenerator = {
isCSTGOptionsValid(maybeOpts, _logWarn) {
if (typeof maybeOpts !== 'object' || maybeOpts === null) {
_logWarn('CSTG opts must be an object');
_logWarn('CSTG is not being used, but is included in the Prebid.js bundle. You can reduce the bundle size by passing "--disable UID2_CSTG" to the Prebid.js build.');
return false;
}

const opts = maybeOpts;
if (!opts.serverPublicKey && !opts.subscriptionId) {
_logWarn('CSTG has been enabled but its parameters have not been set.');
return false;
}
if (typeof opts.serverPublicKey !== 'string') {
_logWarn('CSTG opts.serverPublicKey must be a string');
return false;
Expand Down
14 changes: 14 additions & 0 deletions test/spec/modules/uid2IdSystem_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ const initialToken = `initial-advertising-token`;
const legacyToken = 'legacy-advertising-token';
const refreshedToken = 'refreshed-advertising-token';
const clientSideGeneratedToken = 'client-side-generated-advertising-token';
const optoutToken = 'optout-token';

const legacyConfigParams = {storage: null};
const serverCookieConfigParams = { uid2ServerCookie: publisherCookieName };
const newServerCookieConfigParams = { uid2Cookie: publisherCookieName };
const cstgConfigParams = { serverPublicKey: 'UID2-X-L-24B8a/eLYBmRkXA9yPgRZt+ouKbXewG2OPs23+ov3JC8mtYJBCx6AxGwJ4MlwUcguebhdDp2CvzsCgS9ogwwGA==', subscriptionId: 'subscription-id' }

const makeUid2IdentityContainer = (token) => ({uid2: {id: token}});
const makeUid2OptoutContainer = (token) => ({uid2: {optout: true}});
let useLocalStorage = false;
const makePrebidConfig = (params = null, extraSettings = {}, debug = false) => ({
userSync: { auctionDelay: auctionDelayMs, userIds: [{name: 'uid2', params: {storage: useLocalStorage ? 'localStorage' : 'cookie', ...params}}] }, debug, ...extraSettings
Expand All @@ -49,6 +51,7 @@ const getFromAppropriateStorage = () => {
const expectToken = (bid, token) => expect(bid?.userId ?? {}).to.deep.include(makeUid2IdentityContainer(token));
const expectLegacyToken = (bid) => expect(bid.userId).to.deep.include(makeUid2IdentityContainer(legacyToken));
const expectNoIdentity = (bid) => expect(bid).to.not.haveOwnProperty('userId');
const expectOptout = (bid, token) => expect(bid?.userId ?? {}).to.deep.include(makeUid2OptoutContainer(token));
const expectGlobalToHaveToken = (token) => expect(getGlobal().getUserIds()).to.deep.include(makeUid2IdentityContainer(token));
const expectGlobalToHaveNoUid2 = () => expect(getGlobal().getUserIds()).to.not.haveOwnProperty('uid2');
const expectNoLegacyToken = (bid) => expect(bid.userId).to.not.deep.include(makeUid2IdentityContainer(legacyToken));
Expand All @@ -64,6 +67,7 @@ const apiUrl = 'https://prod.uidapi.com/v2/token'
const refreshApiUrl = `${apiUrl}/refresh`;
const headers = { 'Content-Type': 'application/json' };
const makeSuccessResponseBody = (responseToken) => btoa(JSON.stringify({ status: 'success', body: { ...apiHelpers.makeTokenResponse(initialToken), advertising_token: responseToken } }));
const makeOptoutResponseBody = (token) => btoa(JSON.stringify({ status: 'optout', body: { ...apiHelpers.makeTokenResponse(initialToken), advertising_token: token } }));
const cstgApiUrl = `${apiUrl}/client-generate`;

const testCookieAndLocalStorage = (description, test, only = false) => {
Expand Down Expand Up @@ -120,6 +124,7 @@ describe(`UID2 module`, function () {
const configureUid2Response = (apiUrl, httpStatus, response) => server.respondWith('POST', apiUrl, (xhr) => xhr.respond(httpStatus, headers, response));
const configureUid2ApiSuccessResponse = (apiUrl, responseToken) => configureUid2Response(apiUrl, 200, makeSuccessResponseBody(responseToken));
const configureUid2ApiFailResponse = (apiUrl) => configureUid2Response(apiUrl, 500, 'Error');
const configureUid2CstgResponse = (httpStatus, response) => server.respondWith('POST', cstgApiUrl, (xhr) => xhr.respond(httpStatus, headers, response));
// Runs the provided test twice - once with a successful API mock, once with one which returns a server error
const testApiSuccessAndFailure = (act, apiUrl, testDescription, failTestDescription, only = false, responseToken = refreshedToken) => {
const testFn = only ? it.only : it;
Expand Down Expand Up @@ -442,6 +447,15 @@ describe(`UID2 module`, function () {
});
});
});
it('Should receive an optout response when the user has opted out.', async function() {
const uid2Token = apiHelpers.makeTokenResponse(initialToken, true, true);
configureUid2CstgResponse(200, makeOptoutResponseBody(optoutToken));
config.setConfig(makePrebidConfig({ uid2Token, ...cstgConfigParams, email: 'optout@test.com' }));
apiHelpers.respondAfterDelay(1, server);

const bid = await runAuction();
expectOptout(bid, optoutToken);
});
describe(`when the response doesn't arrive before the auction timer`, function() {
testApiSuccessAndFailure(async function() {
config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test@test.com' }));
Expand Down

0 comments on commit 1e88c2f

Please sign in to comment.