Consent Levels
Overview
It is ethical — and in many places, the law — to obtain consent from users when tracking their behavior on your website. And there can be different levels of consent.
For example:
Strictly Necessary
Analytics
Personalization
Marketing
A user might consent to, or reject, any combination of these levels.
I was heavily involved in the coding of this logic on our website. We created one data element for each consent level, then applied the appropriate data element to each Adobe Launch rule as a condition.
Here’s how the programming works.
OneTrust
OneTrust is a third-party application that we installed on our site. When the user first lands, a OneTrust banner or popup prompts them to select their tracking preferences. Once they do, OneTrust populates the following properties in the data layer with the corresponding Booleans:
dataLayer: {
user: {
consent: {
C0001,
C0002,
C0003,
C0004,
},
},
}
For example, if a user allowed Analytics tracking (Level 2), that property in the data layer would be set to:
dataLayer.user.consent.C0002: true
And if a user did not allow Marketing (Level 4) tracking:
dataLayer.user.consent.C0004: false
These preferences are stored in a browser cookie so that the same user doesn’t need to keep making these selections every time they return to our site.
Consent Level 1: Strictly Necessary
Ironically, the consent level 1 | strictly necessary
data element is going to seem unnecessary from a programming perspective, because all it contains is:
return true;
It doesn’t even check the data layer — it just returns a hardcoded Boolean true
.
Explanation
We do it this way because Level 1 is always allowed; the user cannot turn it off. Adobe Launch rules that fall into this category are not for Analytics, Personalization, or Marketing. They are for functionality, such as triggering slide-ins or popups. And we don’t want that functionality to break if there is a timing issue or glitch in the data layer, so we just hardcode it to true
.
It might also seem unnecessary to bother adding the consent level 1 | strictly necessary
data element as a condition in the applicable Launch rules if it is always going to be true
. But we humans don’t want to keep thinking we forgot to add a consent level condition to those rules every time we open them, so we do ourselves a psychological favor and just add it.
Consent Level 2: Analytics
Because consent can have some pretty strict legal repercussions, our data elements don’t simply pull values straight from the data layer — they do a few custom validations first.
The consent level 2 | analytics
data element uses the following custom code:
let consentLevel2 = false; // Default.
if (window.dataLayer?.user?.consent?.C0002 === true) {
consentLevel2 = true;
}
return consentLevel2;
Explanation
Line 1: Default to
false
. We assume no tracking is allowed until proven otherwise. (What if the user rejected Analytics tracking, but a technical glitch caused our data layer not to load? If we defaulted totrue
, our tracking could illegally fire.)Line 2: Check the
C0002
value in the data layer.NOTE: Optional chaining will throw warnings in Launch because, as of this writing, Launch can only handle up to ES6, and optional chaining is a feature of ES11. Although the code will still run in most browsers, Launch will flag each line of code containing optional chaining. To avoid this distraction, we use a long string of
!!window.dataLayer && !!window.dataLayer.user
etc. in real life. But for the purposes of this case study, I’m shortening it to the optional chaining syntax.Note that we check the value and type (
=== true
). This safeguards against a type glitch causing a string likefalse
to return truthy.
Line 3: If this value was
true
in the data layer, no sense pulling it again — it’s shorter to just hardcode it totrue
at this point.Line 5: Return the result, whether it is still the default of
false
, or it has been overwritten totrue
.
Consent Level 3: Personalization
The main example of Personalization is Adobe Target. We use Adobe Target for A/B tests, and to tailor the site’s contents to known users. For example, if we know that a user was viewing a specific product the last time they visited our website, we can show that product to them when they return, and hopefully reignite that interest.
We’re doing the same validation check we did in the consent level 2 | analytics
data element, plus we only allow Consent Level 3 to fire if Consent Level 2 was also true.
In other words, if the user declined Analytics tracking (Level 2), but allowed Personalization (Level 3), we still don’t fire Personalization.
Here is the code for the consent level 3 | personalization
data element:
let consentLevel3 = false; // Default.
if (
_satellite.getVar('consent level 2 | analytics') === true &&
window.dataLayer?.user?.consent?.C0003 === true
) {
consentLevel3 = true;
}
return consentLevel3;
Consent Level 4: Marketing
The consent level 4 | marketing
data element is for marketing pixels. (We may place ads on Facebook, Google, Pinterest, etc. If those ads drive traffic to the site, we want to track that success.)
Once again, we only allow Marketing (Level 4) to fire if Analytics (Level 2) is also true
. One reason for this is that if there is a question about the metrics from a marketing pixel, we want to be able to match it to the metrics from Adobe Analytics to see how they compare.
let consentLevel4 = false; // Default.
if (
_satellite.getVar('consent level 2 | analytics') === true &&
window.dataLayer?.user?.consent?.C0004 === true &&
_satellite.getVar('consent | sharing allowed') === true &&
!_satellite.getVar('consent | global privacy control')
) {
consentLevel4 = true;
}
return consentLevel4;
Explanation
Lines 3 - 4: These should look familiar by now.
Lines 5 - 6: But marketing pixels share data with third parties. Therefore, we have two more conditions that must be met before we return
true
:Line 5: The user must be allowing sharing. (More on this in the next section.)
Line 6: The user’s browser’s Global Privacy Control must not be active. (More on this in the section after that.)
Sharing Allowed
If a user has created an account on our website, they have the ability to save their sharing preferences. And if they do not want us to share their data with third parties, dataLayer.user.consent.sharingAllowed
will be set to false
.
Our consent | sharing allowed
data element looks to that property, but it also contains custom validation to ensure that it is the proper data type. (When we send this value to AEP, it must be the expected data type, or we will have batch ingestion failures.)
let sharingAllowed = false; // Default.
if (window.dataLayer?.user?.consent?.sharingAllowed === true) {
sharingAllowed = true;
}
return sharingAllowed;
Global Privacy Control
A full explanation of Global Privacy Control can be found here:
https://developer.mozilla.org/en-US/docs/Web/API/Navigator/globalPrivacyControl
For the purposes of this case study, suffice it to say that our consent | global privacy control
data element just needs to return the value of Navigator.globalPrivacyControl
. (It will be true
if the user does not want their data shared with third parties.)
return Navigator.globalPrivacyControl;
Adobe Launch Rule Conditions
Once each data element was programmed and tested, we simply added the appropriate one as a condition to each Launch rule according to the level of consent required to fire that rule.
For example, if a Launch rule fired a marketing pixel, we added the consent level 4 | marketing
data element as one of that rule’s conditions.
And if a Launch rule fired some Adobe Analytics tracking, we added the consent level 2 | analytics
data element as one of that rule’s conditions.