Iubenda logo
Start generating

Documentation

Table of Contents

How to save proof of consent to cookies (Cookie Solution and Consent Solution integration)

In addition to recording GDPR consent for your forms, our Consent Solution also allows you to save consent preferences collected with the Cookie Solution. Below we’ll explain how to save proof of consent to cookies with the combination of both solutions.

If you need to track consent preferences across different devices, jump to the advanced integration method.

Basic integration

Consent Solution

Once you’ve activated the Consent Solution, copy the snippet you find on Dashboard > [Your website] > Consent Solution > Embed and paste it in the HEAD of your pages:

<!-- Consent Solution -->
<script type="text/javascript">
    var _iub = _iub || {};
    _iub.cons_instructions = _iub.cons_instructions || [];
    _iub.cons_instructions.push(["init", {
        api_key: "YOUR_PUBLIC_API_KEY" //use your API key
    }]);
</script>
<script type="text/javascript" src="https://cdn.iubenda.com/cons/iubenda_cons.js" async></script>

The API key (displayed as “YOUR_PUBLIC_API_KEY” in the example above) is a unique code generated by us during the Consent Solution activation and is specific to the particular site.

Cookie Solution

The Consent Solution code will be followed by the Cookie Solution script. Once configured and saved your cookie banner, the Cookie Solution snippet will be similar to this:

<!-- Cookie Solution -->
<script type="text/javascript">
    var _iub = _iub || [];
    _iub.csConfiguration = {
        "lang": "en",
        "siteId": XXXXXX, //use your siteId
        "cookiePolicyId": YYYYYY, //use your cookiePolicyId
        "banner": {
            "position": "float-top-center",
            "acceptButtonDisplay": true,
            "customizeButtonDisplay": true
        }
    };
</script>
<script type="text/javascript" src="//cdn.iubenda.com/cs/iubenda_cs.js" charset="UTF-8" async></script>

In order to pass consent preferences to the Consent Solution we’ll have to define a bannerHTML variable (via which we’ll save the banner HTML to use it as a proof) and a couple of callbacks:

<!-- Cookie Solution -->
<script type="text/javascript">
    var _iub = _iub || [];
    var bannerHTML; //bannerHTML variable
    _iub.csConfiguration = {
        "lang": "en",
        "siteId": 896537, //use your siteId
        "cookiePolicyId": 8207462, //use your cookiePolicyId
        "banner": {
            "position": "float-top-center",
            "acceptButtonDisplay": true,
            "customizeButtonDisplay": true
        },
        "callback": {
            "onReady": function() {
                var banner = document.getElementById('iubenda-cs-banner');
                if (banner) {
                    bannerHTML = banner.innerHTML;
                }
            },
            "onPreferenceFirstExpressed": function(event) {
                _iub.cons_instructions.push(["submit",
                    {
                        consent: {
                            subject: {},
                            preferences: event,
                            legal_notices: [{
                                identifier: "cookie_policy"
                            }],
                            proofs: [{
                                content: JSON.stringify(event),
                                form: bannerHTML
                            }]
                        }
                    }
                ]);
            }
        }
    };
</script>
<script type="text/javascript" src="//cdn.iubenda.com/cs/iubenda_cs.js" charset="UTF-8" async></script>

Once implemented, your Consent Solution dashboard will store all the cookie consent preferences collected through the cookie banner:

Consent Solution dashboard with cookie preferences

You can also store any additional parameters you’d like to add to your cookie consent proofs, such as consent method (e.g. consentOnScroll , consentOnLinkAndButton), purposes etc.

Advanced integration (consent preferences synchronized across multiple devices)

If you need to track preferences of a user across different devices (e.g. website and mobile app), you’ll have to call the Consent Solution at every pageview to keep the preferences constantly in sync among multiple Cookie Solution installations.

Let’s see how to do it.

Consent Solution

As before, once you’ve activated the Consent Solution, copy the snippet you find on Dashboard > [Your website] > Consent Solution > Embed and paste it in the HEAD of your pages:

<!-- Consent Solution -->
<script type="text/javascript">
    var _iub = _iub || {};
    _iub.cons_instructions = _iub.cons_instructions || [];
    _iub.cons_instructions.push(["init", {
        api_key: "YOUR_PUBLIC_API_KEY" //use your API key
    }]);
</script>
<script type="text/javascript" src="https://cdn.iubenda.com/cons/iubenda_cons.js" async></script>

The API key (displayed as “YOUR_PUBLIC_API_KEY” in the example above) is a unique code generated by us during the Consent Solution activation and is specific to the particular site.

Cookie Solution

Again, the Consent Solution code will be followed by the Cookie Solution script. Once configured and saved your cookie banner, the Cookie Solution snippet will be similar to this:

<!-- Cookie Solution -->
<script type="text/javascript">
    var _iub = _iub || [];
    _iub.csConfiguration = {
        "lang": "en",
        "siteId": XXXXXX, //use your siteId
        "cookiePolicyId": YYYYYY, //use your cookiePolicyId
        "banner": {
            "position": "float-top-center",
            "acceptButtonDisplay": true,
            "customizeButtonDisplay": true
        }
    };
</script>
<script type="text/javascript" src="//cdn.iubenda.com/cs/iubenda_cs.js" charset="UTF-8" async></script>

In order to pass consent preferences to the Consent Solution, we’ll have to define two callbacks that are fired by the Cookie Solution to address all cases where a synchronization is needed:

<script type="text/javascript">
    var _iub = _iub || [];
    _iub.csConfiguration = {
        "lang": "en",
        "siteId": XXXXXX, //use your siteId
        "cookiePolicyId": YYYYYY, //use your cookiePolicyId
        "banner": {
            "position": "float-top-center",
            "acceptButtonDisplay": true,
            "customizeButtonDisplay": true
        },
        "callback": {
            "onReady": function() {
                consSyncronizer();
            },
            "onPreferenceFirstExpressed": function(preferences) {
                consSyncronizer();
            }
        }
    };
</script>

Now we’ll need two functions to address three steps:

  1. one function that fires when a page is loaded and before the Cookie Solution activates (needed in order to avoid the banner to show up if a remote consent is present for a given user). We’ll call it consPreflight.
  2. one function that fires when the Cookie Solution activates (onReady callback) and checks whether the device consent and the Consent Solution consent are in sync. We’ll call it consSyncronizer.
  3. consSyncronizer will fire also when the preferences for the Cookie Solution are reopened and changed.

We’ll have to repeat these three steps at each pageview. Let’s see them in a little more detail.

1st step, consPreflight function

In order to avoid the banner to show up if a remote consent is present for a given user, this function queries the Consent Solution for a subject_id (which is used to uniquely identify a user) and tries to retrieve his cookie preferences: 

  • If the subject_id is not found and the local cookie that stores the consent is not found either, the Cookie Solution is activated and the banner is shown. This is most likely the first visit of the user. 
  • If the subject_id is not found but the local cookie is present, it is pushed to the Consent Solution and the returned subject_id is stored. The Cookie Solution is then activated, but the banner is not shown. This is the case for a user that gave consent before this logic has been implemented. 
  • If the subject_id is found and the local cookie is not present, the remote preferences are used to create a local consent cookie. The Cookie Solution is activated and the banner is not shown. This is the case for users that expressed a preference on one instance of the Cookie Solution (e.g. web), and then visited a second instance (e.g. mobile). 
  • Finally, if both the subject_id and the local cookie are found, nothing is done at this stage and the Cookie Solution is activated. This is the most common case for users that keep navigating the site.
function consPreflight() {
  
  // Check if the subject id is available
  var subject_id = localStorage.getItem('subject_id');

  if (subject_id) {
    // Subject id available, check if we need to pull from the Consent Solution before activating the Cookie Solution

    // Try to the fetch the Cookie Solution local cookie
    var cookiePolicyId = _iub.csConfiguration.cookiePolicyId;
    var cookieName = '_iub_cs-' + cookiePolicyId;
    var cookieLocal = getCookie(cookieName);

    if (cookieLocal === null) {
      // No local consent (cookie) found, but a remote consent (Consent Solution) likely exists
      // Fetch the remote consent and create a local consent from it
      // Perform a request for the given subject_id to the Consent Solution
      // WARNING: This uses the private key and should be done in the backend
      $.ajax({
        url: "https://consent.iubenda.com/subjects/" + subject_id,
        type: "GET",
        headers: { "ApiKey": "vs3hcMXwsqrrauIfmW14pFHcUIm9CVvp" }
      })
      .done(function(data, textStatus, jqXHR) {
        var consPreferences = data.preferences;

        // Create the local consent with the remote consent info
        var cookieContent = {timestamp: consPreferences.cookieTimestamp.value, version: consPreferences.cookieVersion.value, purposes: consPreferences.cookiePurposes.value, id: cookiePolicyId};
        setCookie(cookieName, escape(JSON.stringify(cookieContent)), 365);

        if (_iub.csConfiguration.enableTCF) {
        // If the Cookie Solution uses the TCF...
          if (consPreferences.cookieTCFv2) {
            // ...create a cookie for the TCF...
            var cookieTcfName = 'iubeuconsent-v2';
            var cookieTcfContent = consPreferences.cookieTCFv2.value;
            setCookie(cookieTcfName, cookieTcfContent, 365);
          }
        }
      })
      .fail(function(jqXHR, textStatus, errorThrown) {
        // If the request fails, a few retries should be performed before activating the Cookie Solution
        activateCookieSolution();
      })
      .always(function() {
        // Once the request and the syncronization is finished, activate the Cookie Solution
        activateCookieSolution();
      });
    } else {
      // Local consent (cookie) found, we don't need to do any pre-configuration
      activateCookieSolution();
    }
  } else {
    // No subject_id available, so we have nothing to syncronize from
    activateCookieSolution();
  }
}
Important

For demonstration purpose only, we store the subject_id in the localStorage. A production application should bind a user and a subject_id at database level or anyway in a more reliable way than the browser localStorage.

Also, the GET requests to the Consent Solution are possible only using the private key (since the public key is write only). This key should not be disclosed publicly in the frontend of the application. The GET requests should therefore be performed in the backend and the results passed to the Cookie Solution.

2nd step, consSyncronizer function

This function fires when the Cookie Solution activates (onReady callback) and checks whether the device consent and the Consent Solution consent (if present, or if created in one of the previous cases) are in sync.

This is done by comparing the timestamp of the cookie with the Consent Solution preferences: 

  • If the timestamps are equal, no action is performed as the two consents are synchronized. 
  • If the remote timestamp is newer than the local timestamp, the remote preferences are used to update the local preferences. 
  • If the local timestamp is newer than the remote timestamp, the local preferences are pushed to the Consent Solution in order to update the subject.
function consSyncronizer() {
  // Check if the subject id is available
  var subject_id = localStorage.getItem('subject_id');

  // Gather all the local consent info
  var cookiePurposes = _iub.cs.consent.purposes;
  var cookieTimestamp = _iub.cs.consent.timestamp;
  var cookieVersion = _iub.cs.consent.version;
  var cookiePolicyId = _iub.cs.consent.id;

  if (subject_id === null) {
    // No subject_id found...
    if (cookieTimestamp == undefined) {
      // ...and also no local consent. This is most likely a first time visit
      return;
    } else {
      // ...but local consent found. It has probably never been sent to the ConS, so do it now
      var cookiePreferences = {cookiePurposes: cookiePurposes, cookieTimestamp: cookieTimestamp, cookieVersion: cookieVersion, cookiePolicyId: cookiePolicyId};
      tcfAddPreferences(cookiePreferences, function() {
        consPreferencesPost({}, cookiePreferences);
      });

      return;
    }
  }

  // Both a subject_id and a local consent found, check if they need to be synced
  // Query the ConS and retrieve the remote consent for the given subject
  $.ajax({
    url: "https://consent.iubenda.com/subjects/" + subject_id,
    type: "GET",
    headers: { "ApiKey": "vs3hcMXwsqrrauIfmW14pFHcUIm9CVvp" }
  })
  .done(function(data, textStatus, jqXHR) {
    var consPreferences = data.preferences;

    // Convert local and remote timestamps into Unix time for easier comparison
    var consTimestamp = consPreferences.cookieTimestamp.value;
    var cookieUnixTime = new Date(cookieTimestamp).getTime();
    var consUnixTime = new Date(consTimestamp).getTime();

    if (cookieUnixTime == consUnixTime) {
      // Timestamps are equal: the local and remote consents are in sync, do nothing
      return;
    } else if (cookieUnixTime > consUnixTime) {
      // Local timestamp is newer than remote consent: something was changed locally, push the updated consent to ConS
      var cookiePreferences = {cookiePurposes: cookiePurposes, cookieTimestamp: cookieTimestamp, cookieVersion: cookieVersion, cookiePolicyId: cookiePolicyId};
      tcfAddPreferences(cookiePreferences, function() {
        consPreferencesPost({id: subject_id}, cookiePreferences);
      });
    } else {
      // Remote timestamp is newer than local consent: something was changed in another Cookie Solution instance and the local consent is now old, get the updated consent from Consent Solution
      _iub.cs.api.storeConsent({purposes: consPreferences.cookiePurposes.value});

      if (_iub.cs.options.enableTCF) {
        // If the Cookie Solution uses the TCF...
        if (consPreferences.cookieTCFv2) {
          // ...update the cookie for the TCF...
          var cookieTcfName = 'iubeuconsent-v2';
          var cookieTcfContent = consPreferences.cookieTCFv2.value;
          setCookie(cookieTcfName, cookieTcfContent, 365);
        }
      }
    }
  })
  .fail(function(jqXHR, textStatus, errorThrown) {
    console.log("[consSyncronizer] Consent Solution call failed");
  });
}

3rd step, consSyncronizer function (again)

The consSyncronizer function will fire also when the preferences for the Cookie Solution are reopened and changed. This happens when using (for example) a button with the iubenda-cs-preferences-link class that opens the preferences window.

In this case, since both step 1 and step 2 have already been performed, we can safely assume that the local consent and the remote consent are in sync and that the preference change needs to be pushed to the Consent Solution.

Once you’ve implemented these steps along with other additional functions (see our CodePen demo), your Consent Solution dashboard will store all the cookie consent preferences collected through the cookie banner across different devices.

See also