---
schema_version: "1"
id: a127de5f1d5cdf2b0e6e9e9176de12c6970158d7b0a01b8e4fdb7ad5e26389a0
title: Consent Tracking in SFRA
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/commerce-cloud/sfra/guides/b2c-sfra-consent-tracking.md
canonical_url: https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/b2c-sfra-consent-tracking.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: 4293e137641fdc812d3eefaf7b76441b83fb10aff306f8b9a37b8d44ba259d8f
products:
  - Composable Storefront
  - Salesforce APIs
---
# Consent Tracking in SFRA

Implement consent tracking on a storefront based on SFRA. Merchants can track personal information about their shoppers to improve the shopping experience. The merchant can collect and honor shoppers’ consent preferences when they are using the site.

The following example shows one way to implement consent tracking on a storefront adapted from SFRA and is provided for informational purposes only. As always, merchants can consult their own legal department or advisors to review and consent management process.

The implementation uses:

- A content asset for displaying a consent request message to the shopper
- The site-specific preference **Tracking** to disable tracking by default
- A session-specific flag to allow tracking if the shopper grants consent

## Consent Asset for the Consent Request Message

To display a consent message to the shopper, a content asset whose internal ID is `tracking_hint` is used. Replace the placeholder text in the content asset with your own message.

## Tracking Preference

The **Tracking** site preference determines the default tracking behavior. When set to **Opt-in**, personal information is not tracked by default for all shoppers visiting the site; when not set to **Opt-in**, personal information is tracked.

<!--TODO: Fix link post migration -->
<!-- The [**Tracking** site preference](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/admin/b2c-setting-privacy-preferences.md) determines the default tracking behavior. When set to **Opt-in**, personal information is not tracked by default for all shoppers visiting the site; when not set to **Opt-in**, personal information is tracked.
 -->

To set this preference, select **Merchant Tools > _site_ > Site Preferences > Privacy**.

The example assumes the **Tracking** preference is set to **Opt-In**.

## Session Tracking Flag

The example presents a consent request message to the shopper, who can choose to allow tracking. If the shopper allows tracking, it is enabled during the shopper’s session. For more information, see Browser-Based Local Data Storage.

<!--TODO: Fix link post migration -->
<!-- The example presents a consent request message to the shopper, who can choose to allow tracking. If the shopper allows tracking, it is enabled during the shopper’s session. For more information, see [Browser-Based Local Data Storage](https://developer.salesforce.com/dataprotectionandprivacy/localdatastorage.md).
 -->

To enable tracking on a session:

- `dw.system.Session.setTrackingAllowed(boolean)` ― If the boolean value is `true`, tracking is enabled for the current session; if `false`, tracking is disabled.

To check the current value of the session's tracking flag:

- `dw.system.Session.isTrackingAllowed()` ― `true` indicates that tracking is enabled; `false`, disabled.

## Implementation Overview

The sample implementation adds a `<span>` tag to every page in the storefront. The `<span>` tag stores information about the shopper’s consent choices and makes the information available to client-side code running in the browser. If the shopper has not yet given consent, the client-side code inspects the `<span>` tag and displays a Tracking Consent window.

The `<span>` tag is created by the template `app_storefront_base/cartridge/templates/default/common/consent.isml`.

```html
<span
  class="api-${pdict.consentApi} ${pdict.tracking_consent == null ? '' : 'consented' } tracking-consent"
  data-url="${URLUtils.url('ConsentTracking-GetContent', 'cid', 'tracking_hint')}"
  data-reject="${URLUtils.url('ConsentTracking-SetSession', 'consent', 'false')}"
  data-accept="${URLUtils.url('ConsentTracking-SetSession', 'consent', 'true')}"
  data-acceptText="${Resource.msg('button.consentTracking.yes', 'common', null)}"
  data-rejectText="${Resource.msg('button.consentTracking.no', 'common', null)}"
  data-heading="${Resource.msg('heading.consentTracking.track.consent', 'common', null)}"
></span>
```

This template is rendered by the `ConsentTracking-Check` route in the `app_storefront_base/cartridge/controllers/ConsentTracking.js` controller.

```javascript
server.get("Check", consentTracking.consent, function (req, res, next) {
  res.render("/common/consent", {
    consentApi: Object.prototype.hasOwnProperty.call(req.session.raw, "setTrackingAllowed"),
  });
  next();
});
```

This route is called every time a storefront page is rendered. In the route, the middleware step `consentTracking.consent` is invoked before the `consent.isml` template is rendered.

This middleware step is implemented in the server-side script `app_storefront_base/cartridge/scripts/middleware/consentTracking.js`.

```javascript
"use strict";

/**
 * Middleware to use consent tracking check
 * @param {Object} req - Request object
 * @param {Object} res - Response object
 * @param {Function} next - Next call in the middleware chain
 * @returns {void}
 */
function consent(req, res, next) {
  var consented = req.session.privacyCache.get("consent");
  if (consented === null || consented === undefined) {
    req.session.privacyCache.set("consent", null);
  } else if (consented === false) {
    req.session.privacyCache.set("consent", false);
    req.session.raw.setTrackingAllowed(false);
  } else if (consented === true) {
    req.session.privacyCache.set("consent", true);
    req.session.raw.setTrackingAllowed(true);
  }

  res.setViewData({
    tracking_consent: req.session.privacyCache.get("consent"),
  });
  next();
}

module.exports = {
  consent: consent,
};
```

It checks the value of the session's `consent` property. Regardless if the value of the property is either `true` or `false`, it calls the `setTrackingAllowed()` method on the session to record the shopper’s choice.

It then adds the value of the `consent` property to the response's view data, storing it in the `tracking_consent` property. The `tracking_consent` property is made available to the `consent.isml` template via the `pdict` property.

The first time this code is executed in a session, the value of the session's `consent` property is `null`, so the `tracking_consent` property is also null. Because the value of the `tracking_consent` property is `null`, the first line in the `consent.isml` template:

```
<span class="api-${pdict.consentApi} ${pdict.tracking_consent == null ? '' : 'consented' } tracking-consent"
```

Outputs the following HTML:

```
 <span class="api-true tracking-consent" ...
```

In this output, the `consented` attribute value is absent, and its absence indicates to the client-side code that the shopper has not yet consented to tracking. The absence of this attribute causes the client-side code to display the Tracking Consent window, prompting the shopper to grant or deny consent.

After the shopper grants or denies consent, subsequent invocations of the `consentTracking.consent` middleware step set the `tracking_consent` property to a non-null value. As a consequence, the resulting HTML output looks like this code snippet:

```
 <span class="api-true consented tracking-consent" ...
```

In this output, the consented attribute value is present, which indicates to the client-side code that the shopper has either granted or denied access to tracking. Whichever choice the shopper made, it is no longer necessary to display the Tracking Consent window.

Every time the `consent.isml` template is rendered, the `consentApi` property is passed into the template via the `pdict` property. The value of the `consentApi` property is the result of the following call:

```
Object.prototype.hasOwnProperty.call(req.session.raw, 'setTrackingAllowed')
```

The call checks if the session has a `setTrackingAllowed` property. If it does, this call resolves to `true`; otherwise, `false`. This call always evaluates to `true` for Salesforce B2C Commerce version 18.4 and later.

The client-side script, `app_storefront_base/cartridge/client/default/js/components/consentTracking.js`, inspects the `<span>` tag created by the `consent.isml` template.

```javascript
"use strict";

/**
 * Renders a modal window that tracks the shoppers consenting to accepting site
 * tracking policy
 */
function showConsentModal() {
  var urlContent = $(".tracking-consent").data("url");
  var urlAccept = $(".tracking-consent").data("accept");
  var urlReject = $(".tracking-consent").data("reject");
  var textYes = $(".tracking-consent").data("accepttext");
  var textNo = $(".tracking-consent").data("rejecttext");
  var textHeader = $(".tracking-consent").data("heading");

  var htmlString =
    "<!-- Modal -->" +
    '<div class="modal show" id="consent-tracking" role="dialog" style="display: block;">' +
    '<div class="modal-dialog">' +
    "<!-- Modal content-->" +
    '<div class="modal-content">' +
    '<div class="modal-header">' +
    textHeader +
    "</div>" +
    '<div class="modal-body"></div>' +
    '<div class="modal-footer">' +
    '<div class="button-wrapper">' +
    '<button class="affirm btn btn-primary" data-url="' +
    urlAccept +
    '">' +
    textYes +
    "</button>" +
    '<button class="decline btn btn-primary" data-url="' +
    urlReject +
    '">' +
    textNo +
    "</button>" +
    "</div>" +
    "</div>" +
    "</div>" +
    "</div>" +
    "</div>";
  $.spinner().start();

  $("body").append(htmlString);

  $.ajax({
    url: urlContent,
    type: "get",
    dataType: "html",
    success: function (response) {
      $(".modal-body").html(response);
    },
    error: function () {
      $("#consent-tracking").remove();
    },
  });

  $("#consent-tracking .button-wrapper button").click(function (e) {
    e.preventDefault();
    var url = $(this).data("url");
    $.ajax({
      url: url,
      type: "get",
      dataType: "json",
      success: function () {
        $("#consent-tracking .modal-body").remove();
        $("#consent-tracking").remove();
        $.spinner().stop();
      },
      error: function () {
        $("#consent-tracking .modal-body").remove();
        $("#consent-tracking").remove();
        $.spinner().stop();
      },
    });
  });
}

module.exports = function () {
  if ($(".consented").length === 0 && $(".tracking-consent").hasClass("api-true")) {
    showConsentModal();
  }

  if ($(".tracking-consent").hasClass("api-true")) {
    $(".tracking-consent").click(function () {
      showConsentModal();
    });
  }
};
```

This script constructs a section of HTML, stores it in the variable `htmlString`, and appends it to the `<body>` element of the DOM. The script then performs an Ajax call to get the value of the `tracking_hint` content asset. If the call is successful, the script inserts the value of the content asset into the \<div> tag whose class attribute is `modal-body`. If the call fails, the script removes the entire section of appended HTML from the DOM.

The script then creates an event listener on both `<button>` elements in the appended HTML. If the shopper clicks either button in the Tracking Consent window, the script makes a second Ajax call, either enabling or disabling tracking on the session. The script then removes the Tracking Consent window from the DOM.

Lastly, this script exports a function whose purpose is to conditionally call the `showConsentModal()` function. The exported function is invoked in two situations: after each storefront page is fully loaded in the browser window, and when the shopper clicks **Consent to Track** in the Edit Profile form on the My Account page. For more information, see the `app_storefront_base/cartridge/templates/default/account/editProfileForm.isml` template.
