-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Flashtalking FTRACK User ID Submodule: initial release #8063
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
Changes from 7 commits
f6a8fbe
19ccd2b
5da7097
4ba6208
8daacfe
3dfecd8
f86307b
9ef5608
21e5a9a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
/** | ||
* This module adds ftrack to the User ID module | ||
* The {@link module:modules/userId} module is required | ||
* @module modules/ftrack | ||
* @requires module:modules/userId | ||
*/ | ||
|
||
import * as utils from '../src/utils.js'; | ||
import { submodule } from '../src/hook.js'; | ||
import { getStorageManager } from '../src/storageManager.js'; | ||
import { uspDataHandler } from '../src/adapterManager.js'; | ||
|
||
const MODULE_NAME = 'ftrackId'; | ||
const LOG_PREFIX = 'FTRACK - '; | ||
const LOCAL_STORAGE_EXP_DAYS = 30; | ||
const VENDOR_ID = null; | ||
const LOCAL_STORAGE = 'html5'; | ||
const FTRACK_STORAGE_NAME = 'ftrackId'; | ||
const FTRACK_PRIVACY_STORAGE_NAME = `${FTRACK_STORAGE_NAME}_privacy`; | ||
const FTRACK_URL = 'https://d9.flashtalking.com/d9core'; | ||
const storage = getStorageManager(VENDOR_ID, MODULE_NAME); | ||
|
||
let consentInfo = { | ||
gdpr: { | ||
applies: 0, | ||
consentString: null, | ||
pd: null | ||
}, | ||
usPrivacy: { | ||
value: null | ||
} | ||
}; | ||
|
||
/** @type {Submodule} */ | ||
export const ftrackIdSubmodule = { | ||
/** | ||
* used to link submodule with config | ||
* @type {string} | ||
*/ | ||
name: `ftrack`, | ||
|
||
/** | ||
* Decodes the 'value' | ||
* @function decode (required method) | ||
* @param {(Object|string)} value | ||
* @param {SubmoduleConfig|undefined} config | ||
* @returns {(Object|undefined)} an object with the key being ideally camel case | ||
* similar to the module name and ending in id or Id | ||
*/ | ||
decode (value, config) { | ||
return { | ||
ftrackId: value | ||
}; | ||
}, | ||
|
||
/** | ||
* performs action(s) to obtain ids from D9 and return the Device IDs | ||
* should be the only method that gets a new ID (from ajax calls or a cookie/local storage) | ||
* @function getId (required method) | ||
* @param {SubmoduleConfig} config | ||
* @param {ConsentData} consentData | ||
* @param {(Object|undefined)} cacheIdObj | ||
* @returns {IdResponse|undefined} | ||
*/ | ||
getId (config, consentData, cacheIdObj) { | ||
if (this.isConfigOk(config) === false || this.isThereConsent(consentData) === false) return undefined; | ||
|
||
return { | ||
callback: function () { | ||
window.D9v = { | ||
UserID: '99999999999999', | ||
CampID: '3175', | ||
CCampID: '148556' | ||
}; | ||
window.D9r = { | ||
DeviceID: true, | ||
SingleDeviceID: true, | ||
callback: function(response) { | ||
if (response) { | ||
storage.setDataInLocalStorage(`${FTRACK_STORAGE_NAME}_exp`, (new Date(Date.now() + (1000 * 60 * 60 * 24 * LOCAL_STORAGE_EXP_DAYS))).toUTCString()); | ||
storage.setDataInLocalStorage(`${FTRACK_STORAGE_NAME}`, JSON.stringify(response)); | ||
|
||
storage.setDataInLocalStorage(`${FTRACK_PRIVACY_STORAGE_NAME}_exp`, (new Date(Date.now() + (1000 * 60 * 60 * 24 * LOCAL_STORAGE_EXP_DAYS))).toUTCString()); | ||
storage.setDataInLocalStorage(`${FTRACK_PRIVACY_STORAGE_NAME}`, JSON.stringify(consentInfo)); | ||
}; | ||
|
||
return response; | ||
} | ||
}; | ||
|
||
if (config.params && config.params.url && config.params.url === FTRACK_URL) { | ||
var ftrackScript = document.createElement('script'); | ||
ftrackScript.setAttribute('src', config.params.url); | ||
window.document.body.appendChild(ftrackScript); | ||
} | ||
} | ||
}; | ||
}, | ||
|
||
/** | ||
* Called when IDs are already in localStorage | ||
* should just be adding additional data to the cacheIdObj object | ||
* @function extendId (optional method) | ||
* @param {SubmoduleConfig} config | ||
* @param {ConsentData} consentData | ||
* @param {(Object|undefined)} cacheIdObj | ||
* @returns {IdResponse|undefined} | ||
*/ | ||
extendId (config, consentData, cacheIdObj) { | ||
this.isConfigOk(config); | ||
return cacheIdObj; | ||
}, | ||
|
||
/* | ||
* Validates the config, if it is not correct, then info cannot be saved in localstorage | ||
* @function isConfigOk | ||
* @param {SubmoduleConfig} config from HTML | ||
* @returns {true|false} | ||
*/ | ||
isConfigOk: function(config) { | ||
if (!config.storage || !config.storage.type || !config.storage.name) { | ||
utils.logError(LOG_PREFIX + 'config.storage required to be set.'); | ||
return false; | ||
} | ||
|
||
// in a future release, we may return false if storage type or name are not set as required | ||
if (config.storage.type !== LOCAL_STORAGE) { | ||
utils.logWarn(LOG_PREFIX + 'config.storage.type recommended to be "' + LOCAL_STORAGE + '".'); | ||
} | ||
// in a future release, we may return false if storage type or name are not set as required | ||
if (config.storage.name !== FTRACK_STORAGE_NAME) { | ||
utils.logWarn(LOG_PREFIX + 'config.storage.name recommended to be "' + FTRACK_STORAGE_NAME + '".'); | ||
} | ||
|
||
if (!config.hasOwnProperty('params') || !config.params.hasOwnProperty('url') || config.params.url !== FTRACK_URL) { | ||
utils.logWarn(LOG_PREFIX + 'config.params.url is required for ftrack to run. Url should be "' + FTRACK_URL + '".'); | ||
return false; | ||
} | ||
|
||
return true; | ||
}, | ||
|
||
isThereConsent: function(consentData) { | ||
let consentValue = true; | ||
|
||
/* | ||
* Scenario 1: GDPR | ||
* if GDPR Applies is true|1, we do not have consent | ||
* if GDPR Applies does not exist or is false|0, we do not NOT have consent | ||
*/ | ||
if (consentData && consentData.gdprApplies && (consentData.gdprApplies === true || consentData.gdprApplies === 1)) { | ||
consentInfo.gdpr.applies = 1; | ||
consentValue = false; | ||
} | ||
// If consentString exists, then we store it even though we are not using it | ||
if (consentData && consentData.consentString !== 'undefined' && !utils.isEmpty(consentData.consentString) && !utils.isEmptyStr(consentData.consentString)) { | ||
consentInfo.gdpr.consentString = consentData.consentString; | ||
} | ||
|
||
/* | ||
* Scenario 2: CCPA/us_privacy | ||
* if usp exists (assuming this check determines the location of the device to be within the California) | ||
* parse the us_privacy string to see if we have consent | ||
* for version 1 of us_privacy strings, if 'Opt-Out Sale' is 'Y' we do not track | ||
*/ | ||
const usp = uspDataHandler.getConsentData(); | ||
let usPrivacyVersion; | ||
// let usPrivacyOptOut; | ||
let usPrivacyOptOutSale; | ||
// let usPrivacyLSPA; | ||
if (typeof usp !== 'undefined' && !utils.isEmpty(usp) && !utils.isEmptyStr(usp)) { | ||
consentInfo.usPrivacy.value = usp; | ||
usPrivacyVersion = usp[0]; | ||
// usPrivacyOptOut = usp[1]; | ||
usPrivacyOptOutSale = usp[2]; | ||
// usPrivacyLSPA = usp[3]; | ||
} | ||
if (usPrivacyVersion == 1 && usPrivacyOptOutSale === 'Y') consentValue = false; | ||
|
||
return consentValue; | ||
} | ||
}; | ||
|
||
submodule('userId', ftrackIdSubmodule); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
# Flashtalking's FTrack Identity Framework User ID Module | ||
|
||
*The FTrack Identity Framework User ID Module allows publishers to take advantage of Flashtalking's FTrack ID during the bidding process.* | ||
|
||
**THE ONLY COMPLETE SOLUTION FOR COOKIELESS MEASUREMENT & PERSONALIZATION** | ||
|
||
Flashtalking is the world’s first ad serving platform to function without cookies to orchestrate client identity across buy-side ID spaces for measurement and personalization. With over 120 active global advertisers, our cookieless identity framework is market-ready and includes privacy controls to ensure consumer notification and choice on every impression. | ||
smenzer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### [FTrack](https://www.flashtalking.com/identity-framework#FTrack) | ||
|
||
Flashtalking’s cookieless tracking technology uses probabilistic device recognition to derive a privacy-friendly persistent ID for each device. | ||
|
||
**PROVEN** | ||
With over 120 brands using FTrack globally, Flashtalking has accumulated the largest cookieless footprint in the industry. | ||
smenzer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
**ANTI-FINGERPRINTING** | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Most of the code appears to be an attempt to form a fingerprint. The RTD module you provide sends user segments. Is anything in the paragraph below accurate? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Flashtalking's FTrack ID should not be considered fingerprinting software since Fingerprinting, as defined by Google, includes opaque or hidden techniques, tracking in a covert manner, and identity creation without user knowledge or their ability to opt out.
We feel it is extremely important to inform potential partners of the difference between fingerprinting as an opaque, hidden, covert tracking system without user knowledge and transparency and FTrack as a consent-driven, privacy-friendly probabilistic ID. We welcome the opportunity to walk through exactly what FTrack can, does, and doesn't do in terms of user data and our extensive system-wide privacy tools designed to maximize transparency around our tools to users on the open web. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We discussed this in committee and we're not comfortable with the collect signals functionality of your code. You still have a couple of options that could lead to a merge. Many of your peer identity providers simply assume in their submodules that code that is not linked nor contained within this project has run. Eg #7985 provides a reference implementation of both assuming that the publisher has integrated directly with the identity provider, and an implementation that facilitates an integration without referencing the script directly, by making its location a configuration parameter. Many other identity provider reference implementations choose one of these two avenues. If you discover other code in this project collecting signals in the manner of this PR, feel free to flag. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks @patmmccann . I shared your feedback with our "committee" and we will make adjustments based on your feedback. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @patmmccann The config and code have been changed. Hopefully the change successfully addresses the points the committee made about the previous commit. Thanks |
||
FTrack operates in strict compliance with [Google’s definition of anti-fingerprinting](https://blog.google/products/ads-commerce/2021-01-privacy-sandbox/). FTrack does not access PII or sensitive information and provides consumers with notification and choice on every impression. We do not participate in the types of activities that most concern privacy advocates (profiling consumers, building audience segments, and/or monetizing consumer data). | ||
|
||
**GDPR COMPLIANT** | ||
Flashtalking is integrated with the IAB EU’s Transparency & Consent Framework (TCF) and operates on a Consent legal basis where required. As a Data Processor under GDPR, Flashtalking does not combine data across customers nor sell data to third parties. | ||
|
||
**ACCURATE** | ||
FTrack’s broad adoption combined with the maturity of the models (6+ years old) gives Flashtalking the global scale with which to maintain a high degree of model resolution and accuracy. | ||
|
||
**DURABLE** | ||
As new IDs start to proliferate, they will serve as new incremental signals for our models. | ||
smenzer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
--- | ||
|
||
### Support or Maintenance: | ||
|
||
Questions? Comments? Bugs? Praise? Please contact FlashTalking's Prebid Support at [[email protected]](mailto:[email protected]) | ||
|
||
--- | ||
|
||
### FTrack User ID Configuration | ||
|
||
The following configuration parameters are available: | ||
|
||
```javascript | ||
pbjs.setConfig({ | ||
userSync: { | ||
userIds: [{ | ||
name: 'FTrack', | ||
params: { | ||
url: 'https://d9.flashtalking.com/d9core' // required, if not populated ftrack will not run | ||
}, | ||
storage: { | ||
type: 'html5', // "html5" is the required storage type | ||
name: 'FTrackId', // "FTrackId" is the required storage name | ||
expires: 90, // storage lasts for 90 days | ||
refreshInSeconds: 8*3600 // refresh ID every 8 hours to ensure it's fresh | ||
} | ||
}], | ||
auctionDelay: 50 // 50ms maximum auction delay, applies to all userId modules | ||
} | ||
}); | ||
``` | ||
|
||
|
||
|
||
|
||
| Param under userSync.userIds[] | Scope | Type | Description | Example | | ||
| :-- | :-- | :-- | :-- | :-- | | ||
| name | Required | String | The name of this module: `"FTrack"` | `"FTrack"` | | ||
| storage | Required | Object | Storage settings for how the User ID module will cache the FTrack ID locally | | | ||
| storage.type | Required | String | This is where the results of the user ID will be stored. FTrack **requires** `"html5"`. | `"html5"` | | ||
| storage.name | Required | String | The name of the local storage where the user ID will be stored. FTrack **requires** `"FTrackId"`. | `"FTrackId"` | | ||
| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. FTrack recommends `90`. | `90` | | ||
| storage.refreshInSeconds | Optional | Integer | How many seconds until the FTrack ID will be refreshed. FTrack strongly recommends 8 hours between refreshes | `8*3600` | | ||
|
||
**ATTENTION:** As of Prebid.js v4.14.0, FTrack requires `storage.type` to be `"html5"` and `storage.name` to be `"FTrackId"`. Using other values will display a warning today, but in an upcoming release, it will prevent the FTrack module from loading. This change is to ensure the FTrack module in Prebid.js interoperates properly with the [FTrack](https://www.flashtalking.com/identity-framework#FTrack) and to reduce the size of publishers' first-party cookies that are sent to their web servers. If you have any questions, please reach out to us at [[email protected]](mailto:[email protected]). | ||
smenzer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
--- | ||
|
||
### Privacy Policies. | ||
|
||
Complete information available on the Flashtalking [privacy policy page](https://www.flashtalking.com/privacypolicy). | ||
|
||
#### OPTING OUT OF INTEREST-BASED ADVERTISING & COLLECTION OF PERSONAL INFORMATION | ||
|
||
Please visit our [Opt Out Page](https://www.flashtalking.com/optout). | ||
|
||
#### REQUEST REMOVAL OF YOUR PERSONAL DATA (WHERE APPLICABLE) | ||
|
||
You may request by emailing [mailto:[email protected]]([email protected]). | ||
|
||
#### GDPR | ||
|
||
In its current state, Flashtalking’s FTrack Identity Framework User ID Module does not create an ID if a user's consentData is "truthy" (true, 1). In other words, if GDPR applies in any way to a user, FTrack does not create an ID. |
Uh oh!
There was an error while loading. Please reload this page.