Skip to content

Commit

Permalink
pc/consent: Fides.meta integration function (#2217)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssangervasi authored Jan 24, 2023
1 parent c4c6ed9 commit 02a6af3
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ The types of changes are:
* Privacy Center `fides-consent.js`:
* `Fides.shopify` integration function. [#2152](https://github.com/ethyca/fides/pull/2152)
* Dedicated folder for integrations.
* `Fides.meta` integration function (fbq). [#2217](https://github.com/ethyca/fides/pull/2217)
* Adds support for Twilio email service (Sendgrid) [#2154](https://github.com/ethyca/fides/pull/2154)
* Access and erasure support for Recharge [#1709](https://github.com/ethyca/fides/pull/1709)
* Access and erasure support for Friendbuy Nextgen [#2085](https://github.com/ethyca/fides/pull/2085)
Expand Down
4 changes: 2 additions & 2 deletions clients/privacy-center/config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"url": "https://example.com/privacy#email-marketing",
"default": true,
"highlight": false,
"cookieKeys": []
"cookieKeys": ["tracking"]
},
{
"fidesDataUseKey": "improve",
Expand All @@ -63,7 +63,7 @@
"url": "https://example.com/privacy#analytics",
"default": true,
"highlight": false,
"cookieKeys": []
"cookieKeys": ["tracking"]
}
]
}
Expand Down
51 changes: 50 additions & 1 deletion clients/privacy-center/cypress/e2e/consent.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,14 @@ describe("Consent settings", () => {
});

it("reflects their choices using fides-consent.js", () => {
// Opt-out of a an item defaults to opt-in.
// Opt-out of items default to opt-in.
cy.visit("/consent");
cy.getByTestId(`consent-item-card-advertising`).within(() => {
cy.getRadio("false").check({ force: true });
});
cy.getByTestId(`consent-item-card-improve`).within(() => {
cy.getRadio("false").check({ force: true });
});
cy.getByTestId("save-btn").click();

cy.visit("/fides-consent-demo.html");
Expand All @@ -123,7 +126,30 @@ describe("Consent settings", () => {
// Now all of the cookie keys should be populated.
expect(win).to.have.nested.property("Fides.consent").that.eql({
data_sales: false,
tracking: false,
});

// GTM configuration
expect(win)
.to.have.nested.property("dataLayer")
.that.eql([
{
Fides: {
consent: {
data_sales: false,
tracking: false,
},
},
},
]);

// Meta Pixel configuration
expect(win)
.to.have.nested.property("fbq.queue")
.that.eql([
["consent", "revoke"],
["dataProcessingOptions", ["LDU"], 1, 1000],
]);
});
});
});
Expand All @@ -136,7 +162,30 @@ describe("Consent settings", () => {
// Before visiting the privacy center the consent object only has the default choices.
expect(win).to.have.nested.property("Fides.consent").that.eql({
data_sales: true,
tracking: true,
});

// GTM configuration
expect(win)
.to.have.nested.property("dataLayer")
.that.eql([
{
Fides: {
consent: {
data_sales: true,
tracking: true,
},
},
},
]);

// Meta Pixel configuration
expect(win)
.to.have.nested.property("fbq.queue")
.that.eql([
["consent", "grant"],
["dataProcessingOptions", []],
]);
});
});
});
Expand Down
68 changes: 63 additions & 5 deletions clients/privacy-center/packages/fides-consent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,36 @@ For example, the default configuration includes a consent option for advertising
When a user visits a page that includes `fides-consent.js` with this configuration, the value of
`Fides.consent.data_sales` will be set to `true` by default.

If the user visits the Privacy Center and removes their consent for advertising, this choice is
If the user visits the Privacy Center and removes their consent for `advertising`, this choice is
saved in their browser. Subsequent visits to pages that include `fides-consent.js` will have
`Fides.consent.data_sales` set to `false`.

Multiple data uses can refer to the same cookie key. A user must consent to _all_ of those data uses
for the cookie key to be true. For example, with the default configuration:

```json
{
"consent": {
"consentOptions": [
{
"fidesDataUseKey": "advertising.first_party",
"name": "Email Marketing",
"default": true,
"cookieKeys": ["tracking"]
},
{
"fidesDataUseKey": "improve",
"name": "Product Analytics",
"default": true,
"cookieKeys": ["tracking"]
}
]
}
}
```

By default, `Fides.consent.tracking` will be set to `true`. If the user removes their consent for
`advertising.first_party`, `improve`, or both, then `Fides.consent.tracking` will be set to `false`.

## Integrations

Expand Down Expand Up @@ -85,24 +111,56 @@ if (Fides.consent.data_sales) {
}
```

### Meta Pixel


To integrate with [Meta's tracking pixel](https://developers.facebook.com/docs/meta-pixel/get-started),
call `Fides.meta(options)`, where options is an object with the following properties:

- `consent`: boolean - Whether the user consents to [Meta's use of cookies](https://developers.facebook.com/docs/meta-pixel/implementation/gdpr).
If `true`, consent it granted. Otherwise, consent is revoked.
- `dataUse`: boolean - Whether user's data can be [used by Meta](https://developers.facebook.com/docs/meta-pixel/implementation/ccpa).
If `true`, Meta may use the data. Otherwise, Meta's Limited Data Use (LDU) mode will be be applied
as if the user were in California.

For example, with the default Privacy Center configuration:

```html
<head>
<script src="example.com/privacy-center/fides-consent.js"></script>
<script>
Fides.meta({
consent: Fides.consent.tracking,
dataUse: Fides.consent.data_sales
})
</script>

<!-- Include Meta's pixel code below-->
<head>
```

This example omits [Meta's pixel code](https://developers.facebook.com/docs/meta-pixel/get-started).
`Fides.meta()` should be included before Meta's code, before your use of
`fbq('init', '{your-pixel-id-goes-here}')`.

### Shopify

To integrate with Shopify's [Consent Tracking API](https://shopify.dev/api/consent-tracking?shpxid=7e81a186-C696-4E23-F327-E7F38E5FF5EE#consent-collection),
call `Fides.shopify(options)`, where options is an with the following properties:
call `Fides.shopify(options)`, where options is an object with the following properties:

- `tracking`: The only consent option Shopify currently supports. Refer to their [visitor tracking](https://shopify.dev/api/consent-tracking#visitor-tracking) documentation.
- `tracking`: boolean - The only consent option Shopify currently supports. Refer to their [visitor tracking](https://shopify.dev/api/consent-tracking#visitor-tracking) documentation.

For example, with the default Privacy Center configuration:

```html
<head>
<!-- The script can be loaded in the store's theme, or in a custom pixel. -->
<script src="example.com/privacy-center/fides-consent.js"></script>
<script>Fides.shopify({ tracking: Fides.consent.data_sales })</script>
<script>Fides.shopify({ tracking: Fides.consent.tracking })</script>
<head>
```

Note that `data_sales` is just an example cookie key. You may configure other data uses that should
Note that `Fides.consent.tracking` is just an example cookie key. You may configure other data uses that should
be considered tracking, whose cookie key you would pass as the `tracking` option instead.

## fides-consent.mjs & fides-consent.d.ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import consentConfig from "./consent-config.json";

import { gtm } from "./integrations/gtm";
import { meta } from "./integrations/meta";
import { shopify } from "./integrations/shopify";
import { getConsentCookie } from "./lib/cookie";

Expand All @@ -17,6 +18,7 @@ const Fides = {
consent: getConsentCookie(consentConfig.defaults),

gtm,
meta,
shopify,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ declare global {
*/
export const gtm = () => {
const dataLayer = window.dataLayer ?? [];
window.dataLayer = dataLayer;
dataLayer.push({
Fides: {
consent: window.Fides.consent,
Expand Down
103 changes: 103 additions & 0 deletions clients/privacy-center/packages/fides-consent/src/integrations/meta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
declare global {
namespace Meta {
type DataProcessingOption = "LDU";

/**
* Facebook doesn't document the many types of arguments this function accepts,
* they just provides some examples, mainly of the 'track' action.
*
* Consent and Limited Data Use have their own examples:
* - https://developers.facebook.com/docs/meta-pixel/implementation/gdpr
* - https://developers.facebook.com/docs/meta-pixel/implementation/ccpa
* - https://developers.facebook.com/docs/marketing-apis/data-processing-options/
*/
type FBQMethod = {
(
method: "dataProcessingOptions",
options: Array<DataProcessingOption>,
country?: 0 | 1,
state?: 0 | 1000
): void;
(method: "consent", consent: "revoke" | "grant"): void;
(method: "init", pixelId: string): void;
(method: string, ...args: any[]): void;
};

type FBQObj = {
loaded: boolean;
version: string;
queue: any[];

callMethod?: FBQMethod;
push: (...args: Parameters<FBQMethod>) => void;
};

type FBQ = FBQMethod & FBQObj;
}

interface Window {
fbq?: Meta.FBQ;
_fbq?: Meta.FBQ;
}
}

/**
* Facebook handles the possibility of the API script being loaded after attempts to call
* `fbq(args)` by shimming `window.fbq` with a function that just enqueues the args to be called
* later.
*/
const getFbq = (): Meta.FBQ => {
if (window.fbq) {
return window.fbq;
}

const shim: Meta.FBQObj = {
queue: [],
loaded: true,
version: "2.0",

push(...args) {
const fbq = window.fbq!;
if (fbq.callMethod) {
fbq.callMethod(...args);
} else {
fbq.queue.push(args);
}
},
};

window.fbq = Object.assign(shim.push, shim);

// eslint-disable-next-line no-underscore-dangle
window._fbq = window.fbq;

return window.fbq;
};

type MetaOptions = {
consent: boolean | undefined;
dataUse: boolean | undefined;
};

/**
* Call Fides.meta to configure Meta Pixel tracking.
*
* @example
* Fides.meta({
* consent: Fides.consent.data_sales,
* dataUse: Fides.consent.data_sales,
* })
*/
export const meta = (options: MetaOptions) => {
const fbq = getFbq();

fbq("consent", options.consent ? "grant" : "revoke");

if (options.dataUse) {
fbq("dataProcessingOptions", []);
} else {
// The integer arguments translate to "treat this user as if they are in California" which will
// limit the use of their data.
fbq("dataProcessingOptions", ["LDU"], 1, 1000);
}
};
7 changes: 7 additions & 0 deletions clients/privacy-center/public/fides-consent-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ <h2>Consent JSON</h2>
null,
2
);

// Test behavior of integrations that can be configured without/before their platform scripts.
Fides.gtm();
Fides.meta({
consent: Fides.consent.tracking,
dataUse: Fides.consent.data_sales,
});
})();
</script>
</html>

0 comments on commit 02a6af3

Please sign in to comment.