GDPR & TCF v2 Setup and Configuration

Overview

Implementing the Sourcepoint Consent Management Platform (CMP) for GDPR & TCF v2 follows a simple two step process that enables publishers and website owners to get up and running with minimal time and resource investment. This document is a guide on how to configure the Sourcepoint code snippet that enables the display of TCF v2 compliant user notifications as well as the syndication of consent signals to vendors through the TCF's v2 onsite API. This guide provides details on the technical implementation steps, however your Sourcepoint account dashboard needs to be enabled for TCF v2 before you can begin the implementation process. Please reach out to your account manager to learn more about this upcoming release and how to join the closed beta program.

1. Two step process to implement the GDPR & TCF v2 code snippet

  1. Implementing the Sourcepoint CCPA page configuration.

  2. Setting up the message domain.

Below is the Sourcepoint’s GDPR & TCF v2 Javascript code-snippet which needs to be placed at the top of the HTML document and before any ad-tech related code snippets such as a header bidding script. It is strongly recommended to implement the Sourcepoint code snippet between the <head></head> tags of the HTML document to ensure the correct execution of vendor tags that depend on the timely availability of the consent signal.

<script type="text/javascript">
!function (t) { var e = {}; function n(r) { if (e[r]) return e[r].exports; var o = e[r] = { i: r, l: !1, exports: {} }; return t[r].call(o.exports, o, o.exports, n), o.l = !0, o.exports } n.m = t, n.c = e, n.d = function (t, e, r) { n.o(t, e) || Object.defineProperty(t, e, { enumerable: !0, get: r }) }, n.r = function (t) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(t, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(t, "__esModule", { value: !0 }) }, n.t = function (t, e) { if (1 & e && (t = n(t)), 8 & e) return t; if (4 & e && "object" == typeof t && t && t.__esModule) return t; var r = Object.create(null); if (n.r(r), Object.defineProperty(r, "default", { enumerable: !0, value: t }), 2 & e && "string" != typeof t) for (var o in t) n.d(r, o, function (e) { return t[e] }.bind(null, o)); return r }, n.n = function (t) { var e = t && t.__esModule ? function () { return t.default } : function () { return t }; return n.d(e, "a", e), e }, n.o = function (t, e) { return Object.prototype.hasOwnProperty.call(t, e) }, n.p = "", n(n.s = 3) }([function (t, e) { t.exports = function (t) { return "object" == typeof t ? null !== t : "function" == typeof t } }, function (t, e, n) { t.exports = !n(2)(function () { return 7 != Object.defineProperty({}, "a", { get: function () { return 7 } }).a }) }, function (t, e) { t.exports = function (t) { try { return !!t() } catch (t) { return !0 } } }, function (t, e, n) { "use strict"; n.r(e); n(4); !function () { if ("function" != typeof window.__tcfapi) { var t, e = [], n = window, r = n.document; !n.__tcfapi && function t() { var e = !!n.frames.__tcfapiLocator; if (!e) if (r.body) { var o = r.createElement("iframe"); o.style.cssText = "display:none", o.name = "__tcfapiLocator", r.body.appendChild(o) } else setTimeout(t, 5); return !e }() && (n.__tcfapi = function () { for (var n = arguments.length, r = new Array(n), o = 0; o < n; o++)r[o] = arguments[o]; if (!r.length) return e; if ("setGdprApplies" === r[0]) r.length > 3 && 2 === parseInt(r[1], 10) && "boolean" == typeof r[3] && (t = r[3], "function" == typeof r[2] && r[2]("set", !0)); else if ("ping" === r[0]) { var i = { gdprApplies: t, cmpLoaded: !1, apiVersion: "2.0" }; "function" == typeof r[2] && r[2](i, !0) } else e.push(r) }, n.addEventListener("message", function (t) { var e = "string" == typeof t.data, r = {}; try { r = e ? JSON.parse(t.data) : t.data } catch (t) { } var o = r.__tcfapiCall; o && n.__tcfapi(o.command, o.parameter, o.version, function (n, r) { var i = { __tcfapiReturn: { returnValue: n, success: r, callId: o.callId } }; e && (i = JSON.stringify(i)), t.source.postMessage(i, "*") }) }, !1)) } }() }, function (t, e, n) { var r = n(5).f, o = Function.prototype, i = /^\s*function ([^ (]*)/; "name" in o || n(1) && r(o, "name", { configurable: !0, get: function () { try { return ("" + this).match(i)[1] } catch (t) { return "" } } }) }, function (t, e, n) { var r = n(6), o = n(7), i = n(10), f = Object.defineProperty; e.f = n(1) ? Object.defineProperty : function (t, e, n) { if (r(t), e = i(e, !0), r(n), o) try { return f(t, e, n) } catch (t) { } if ("get" in n || "set" in n) throw TypeError("Accessors not supported!"); return "value" in n && (t[e] = n.value), t } }, function (t, e, n) { var r = n(0); t.exports = function (t) { if (!r(t)) throw TypeError(t + " is not an object!"); return t } }, function (t, e, n) { t.exports = !n(1) && !n(2)(function () { return 7 != Object.defineProperty(n(8)("div"), "a", { get: function () { return 7 } }).a }) }, function (t, e, n) { var r = n(0), o = n(9).document, i = r(o) && r(o.createElement); t.exports = function (t) { return i ? o.createElement(t) : {} } }, function (t, e) { var n = t.exports = "undefined" != typeof window && window.Math == Math ? window : "undefined" != typeof self && self.Math == Math ? self : Function("return this")(); "number" == typeof __g && (__g = n) }, function (t, e, n) { var r = n(0); t.exports = function (t, e) { if (!r(t)) return t; var n, o; if (e && "function" == typeof (n = t.toString) && !r(o = n.call(t))) return o; if ("function" == typeof (n = t.valueOf) && !r(o = n.call(t))) return o; if (!e && "function" == typeof (n = t.toString) && !r(o = n.call(t))) return o; throw TypeError("Can't convert object to primitive value") } }]);
</script>
<script>
window._sp_ = {
config: {
accountId: ACCOUNT_ID_HERE,
wrapperAPIOrigin: "https://wrapper-api.sp-prod.net/tcfv2",
mmsDomain: "https://message.sp-prod.net"
}
}
</script>
<script src="https://gdpr-tcfv2.sp-prod.net/wrapperMessagingWithoutDetection.js"></script>

1. The first section of the snippet contains the so called IAB Stub file. The Stub file is defining the "__tcfapi" function to queue all calls into the CMP's onsite API to be released as soon as the consent information is available. It is important to have this script tag always at the top of the HTML document in the first position to avoid errors and failure of the service. To learn more about the IAB Stub file please refer to the IAB's TCF v2 Technical Specifications.

2. The second section of the snippet contains your account specific configuration parameters. This section sets up the parameters necessary for your website to communicate with the Sourcepoint messaging platform and establishes a communication channel with the Sourcepoint messaging and consent service libraries. In addition to the standard parameters in the example above, there are additional parameters that allow for Javascript callbacks to be triggered for different customization purposes. For GDPR & TCF v2 implementations there are currently three required paramameters to deliver a user notification successfully:

a. mmsDomain - For maintenance and optimization reasons the mmsDomain is account-specific. All mms Domains follow the pattern 'https://message{accountIdHere}.sp-prod.net' where {accountIdHere} needs to be replaced with your individual Sourcepoint account ID (see below). The mmsDomain can also be changed to a CNAMED 1st party subdomain in order to persist 1st party cookies on Safari web browser (due to Safari’s ITP) by setting cookies through the server with "set-cookie" rather than using "document.cookie" on the page. Changing the mmsDomain is optional! More information about setting up an mmsDomain below.

b. wrapperAPIOrigin - "https://wrapper-api.sp-prod.net/tcfv2" is the endpoint from where the GDPR & TCF v2 service is served. Keep as is.

c. accountId – This parameter needs to be used to set the account ID you received from your Sourcepoint account manager - The ID associates your data and website with the your account in the Sourcepoint dashboard.

In addition to the required parameters you can use following optional configuration parameters to tailor the implementation to your use case.

siteHref – Maps the implementation to a specific URL as set up in the Sourcepoint account dashboard.

propertyId – Maps the message to a specific property (website, app, OTT) as set up in Sourcepoint account dashboard.

targetingParams –This parameter enables you to create key-value pairs that can be used for targeting in the scenario builder in the Sourcepoint dashboard. Key-value pairs can be created in the following format:

targetingParams: {
key1: valueA,
key2: valueB
}

events – An array of events that allow Javascript callbacks to be triggered. Please refer to the Optional Callback document to learn more about how to use events as part of your setup configuration.

2. Creating a 1st party subdomain CNAME for the mmsDomain

Creating a 1st party subdomain for the messaging domain (mmsDomain) and setting up a CNAME to the Sourcepoint endpoint is optional. The goal of creating a mmsDomain is for the CCPA Javascript library to communicate with the Sourcepoint messaging server through a 1st party subdomain. The benefit of this approach is to allow Sourcepoint cookies to be “first party” and thus, circumventing Safari’s Intelligent Tracking Prevention (ITP). This creates a discrete messaging channel between the publisher’s messaging subdomain and the Sourcepoint messaging server. Once you create the subdomain, you should create a DNS CNAME record to direct traffic to the Sourcepoint messaging endpoint message<account id>.sp-prod.net where the account ID refers to your account ID in the Sourcepoint user interface. If you are unsure of what your account ID is, please contact your Sourcepoint account manager.

Once you have created the CNAME record, please inform your Sourcepoint Account manager so that they can create an SSL certificate for the subdomain. This will ensure that both secure and non-secure traffic is handled properly.