-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Prebid.js, UnifiedID, and other user IDs #3046
Comments
@bretg What's your thinking about whether the default config is before the bid request or after? For sync delay for bid request, is there a reason to have separate timer? An option that keeps things simpler would be run the OpenID sync with the rest of the adapter syncs. |
Discussed with some members of OpenID consortium and RubiconProject internal. Changed:
|
Looks like a great start - thanks Bret!
Not sure if that's a use case we'd ever need to support though. |
This looks similar to the already existing pubCommonId module so we should probably make this generic enough to support multiple implementations. Right now the pubCommonId uses the requestBids hook to add its extension. Don't know if we want to do something similar here or add a more specific extension point for shared ids. |
+1 to Rich's comment. I think we can cover pubCommonId with the proposed structure above. Edit: We'd probably remove pubCommonId as a separate module and either
|
thanks for the input. Description updated:
|
@mkendall07 I think it's worth making the mechanism generic enough to support multiple providers given that's far from clear which id providers publishers will want to use, especially with AppNexus leaving the Advertising ID Consortium |
@mike-chowla yes Bret updated the proposal to be generic. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Made an update to move the "url" attribute underneath a "params" object and add a "partner" attribute as well. Looks like the TradeDesk is going to allow us to use their URL as a default for OpenId - will post that once confirming. Once that happens, |
Looks good. It's great that GDPR for publishers will be taken into account. A few questions from pubCommonId perspective.
|
Ok - I took a cut at integrating all features of PubCommonID into this proposal. @mkendall07 - this is now complicated enough to warrant a review. Perhaps we can do that in the next PBJS PMC meeting? |
Spoke with DigiTrust - added a couple of minor tweaks to support their involvement in this. |
Hello. I've recently come on to supporting the DigiTrust id system via IAB Tech Lab. One of our high priorities is integration with Prebid, so to that end I'll be picking everyone's brains on how to do that correctly. In reading this thread it isn't clear if a consensus was reached. Is the plan to build on pubCommonId module, or to implement the pseudo code for UniversalId outlined in the spec? Forgive me if some questions appear dense. I'm just starting to dig into this code. |
@goosemanjack - we're building this new "universal ID" module that has a a simple requirement for supporting each additional ID system must be implementable using a small amount of code, say ~0.25 KB. The framework does most of the work -- the only code that each ID system provides is:
Isaac has provided much of the general code already in the opening comment - it seems likely that we'll release with OpenID and PubCommon, and when you have DigiTrust ready we'll add it. If you have a webservice all ready to go, then you can pile on to this release. |
Great. In most cases the DigiTrust ID will be cryptographically generated on the browser client. Some edge cases with Chrome over non-ssl connections require our web service. We may need to talk more about your file size constraints. Use of DigiTrust IDs is restricted to members and we utilize an iframe to make the ID portable across different member publisher sites without the need for sync calls or server hits. The framework also provides some functionality around GDPR compliance and opt-out that we should insure is otherwise covered in Prebid or integrated from DigiTrust into Prebid. On a more technical note, it appears prebid is using ES6 syntax for modules. We are currently CommonJS/ES5. It is my understanding that we will need to upgrade to ES6 syntax in order to integrate into your build system. If anyone has insight where that wouldn't be the case, please LMK. Thanks. |
this is LGTM from a spec proposal - I'd have some comments on the implementation but let's do that as a formal review process. @bretg Feel free to open the PR. |
They renamed OpenId to UnifiedId. Updated. |
Note: added a feature to let the gdprConsent object be available to the getId function. We'll update the unifiedId getId function to use it |
@pycnvr noted in prebid/prebid.github.io#1068 that the PubCommonID requires an "optout" cookie flag, which is the per-user ability to avoid setting IDs for sites that don't have a GDPR-compliant CMP. Added this item above:
|
Draft documentation at https://staging.prebid.org/dev-docs/modules/universalid.html |
@bretg https://staging.prebid.org/dev-docs/modules/universalid.html - 404 when building PB with userID from download page - {"error":"Prebid file not built properly","requestId":"d99fd837-0fa9-41b4-8fa6-98386173c118"} |
@bretg: Is there a timeline for release of this module? |
Actually, it was released on Weds with PBJS 2.10. Documentation at https://prebid.org/dev-docs/modules/userId.html Currently only two adapters support Unified ID - would love to see others add support. |
Overview
UnifiedID is a way of establishing 'universal' user device IDs for web browsers instead of having dozens of exchanges sync IDs with hundreds of demand sources. The basic concept is that the UnifiedID organization stores a set of global IDs at several locations and each publisher using Prebid.js should be able to take advantage of UnifiedID to get better bids from supporting DSPs.
Note that UnifiedID is not a single ID, but potentially a set of IDs. At first just from The TradeDesk, but someday perhaps others.
There are several main steps in the process:
Note that universal user IDs aren't needed in the mobile app world because device ID is available in those ad serving scenarios.
Prebid, Unified ID, and other User IDs
Prebid.org intends to support integration of Unified ID and other 'universal' IDs as a core feature in header bidding products with appropriate publisher-level controls. We will also deprecate the "pubCommonId", folding it's functionality into this more generic module. (The existing module will be left for a few months as there will be page changes to make.)
Prebid support of IDs should include:
gdprConsent
settings for the userThe role of Prebid.js is to:
The role of Prebid Server is to:
Then it's up to each adapter to read the ID values from a standard location and forward it through their pipeline.
Use Cases
Examples of the proposed publisher configuration for various scenarios follows:
Design Notes
Prebid.js will use the setConfig values configured by the publisher to look for locally stored ID values. If it doesn't find local IDs, it will reach out to the configured URLs, storing any results for the specified number of days as appropriate after checking for GDPR consent.
The prebidServerBidAdapter adds the values to the user.ext.eids section of the OpenRtb2 protocol:
/**
*/
/**
*/
/**
*/
/**
*/
/**
*/
const pubCommonId = {
configKey: 'pubcid', expires: 2628000, decode: function(idData) {
return {
'ext.pubcommonid': idData
}
}, getId: function(data, callback) {
if (data.params.url) {
ajax(data.params.url, function(response) {
callback(response)
})
} else {
// log error, missing required param
}
}
}
/**
*/
const unifiedId = {
configKey: 'unifiedid', expires: 20000, decode: function(idData) {
return {
'ext.unifiedid': idData
}
}, getId: function(url, syncDelay, callback) {
callback('response data')
}
}
/**
*/
const extendedBidRequestData = []
/**
Decorate ad units with user id properties. This hook function is called before the
real pbjs.requestBids is invoked, and can modify its parameter
@param {PrebidConfig} config
@param next
@returns {*}
*/
function requestBidHook (config, next) {
// Note: calling next() allows Prebid to continue processing an auction, if not called, the auction will be stalled.
// pass id data to adapters if bidRequestData list is not empty
if (extendedBidRequestData.length) {
const data = extendedBidRequestData.reduce(function(aggregate, item) {
Object.keys(item).forEach(function(propName) {
aggregate[propName] = item[propName]
return aggregate
})
}, {})
}
return next.apply(this, arguments)
}
/**
*/
function localStorageEnabled () {
return typeof localStorage === 'object' || utils.cookiesAreEnabled()
}
/**
*/
function saveLocalStorageValue (data, name, storageType, expires) {
try {
if (storageType === 'localStorage' && localStorage) {
// since local storage does not have expiration built in, it will need to be emulated by adding a property
// named expiration and when reading from local storage it will need to be compared against the current date.
// if the current date is past the expiration, the local storage key should be deleted
localStorage.setItem(name, data)
} else {
// we need to discuss if we want to automatically fallback to cookie if local storage is not available
setCookie(name, data, expires)
}
} catch (e) {
// log error saving to local storage
return
}
return true
}
/**
*/
function getLocalStorageValue (name, type) {
try {
if (type === 'localStorage' && localStorage) {
return localStorage.getItem(name)
} else {
return getCookie(name)
}
} catch (e) {
// log error
}
}
/**
*/
function setCookie (name, value, expires) {
const expTime = new Date()
expTime.setTime(expTime.getTime() + expires * 1000 * 60)
window.document.cookie = name + '=' + encodeURIComponent(value) + ';path=/;expires=' + expTime.toGMTString()
}
/**
/
function getCookie (name) {
const m = window.document.cookie.match('(^|;)\s' + name + '\s=\s*([^;])\s(;|$)')
return m ? decodeURIComponent(m[2]) : null
}
/**
*/
function addIdDataToBids (idData, submodule) {
const bidRequestIdData = submodule.decode(idData)
if (bidRequestIdData) {
// add id data for appending to bidRequests
extendedBidRequestData.push(bidRequestIdData)
} else {
// log no value decoded for id submodule
}
}
/**
ID submodule getId complete handler
@param {Object|string|number} response
@param {Object} data - config data for sub-moduel
@param {IdSubmodule} submodule
*/
function getIdComplete (response, data, submodule) {
if (response) {
} else {
// log error web request for id failed
}
}
/**
init user id module if config values are set correctly
*/
function initUserId () {
// check if cookie/local storage is active
if (!localStorageEnabled()) {
// exit if no cookies or local storage
return
}
// check if any user id types are set in configuration (must opt-in to enable)
if (!Array.isArray(config.get('usersync.userIds'))) {
// exit if no configurations are set
return
}
[pubCommonId, unifiedId].forEach(function(submodule) {
// try to get config for id submodule
const submoduleConfig = config.get('usersync.userIds').find(userIdConfig => userIdConfig.name === submodule.configKey)
if (!submoduleConfig) {
// log error, config not found for submodule, skip this id submodule
return
}
})
// add hook only if data is going to be passed at this point, else add hook in getId complete
$$PREBID_GLOBAL$$ .requestBids.addHook(requestBidHook)
if (extendedBidRequestData.length) {
}
}
// call init
initUserId()
The text was updated successfully, but these errors were encountered: