Skip to content

hmrc/tracking-consent-frontend

Repository files navigation

Tracking Consent Frontend

Tracking Consent Frontend is a solution intended to address the need for users visiting the public-facing HMRC tax platform (www.tax.service.gov.uk) to provide their explicit informed consent to the use of non-essential cookies.

Tracking consent provides a means to capture consent but is not itself responsible for ensuring this consent is correctly honoured by consuming services. Tracking consent does not actively allow or block cookies nor does it conditionally enable services such as Google Analytics or Optimizely.

Tracking consent provides:

  1. A Javascript asset intended to be included by all frontend microservices running under www.tax.service.gov.uk to
    1. provide an integration with Google Tag Manager (GTM), the service used to centrally control the enablement of Google Analytics on the tax platform
    2. inject a banner for tax users to accept or reject additional cookies, storing these preferences in a userConsent cookie and notifying subscribed services on page load and following changes to them
  2. a cookie settings page that allows tracking preferences to be adjusted

Should my service be integrating with tracking-consent-frontend?

If you have a public-facing microservice running under www.tax.service.gov.uk, you should integrate with the tracking-consent-frontend microservice to provide an opt-in banner for cookie consent.

How do I integrate with tracking consent?

Tracking consent is designed to be used in conjunction with HMRC's frontend libraries.

Integration guidelines for play-frontend-hmrc can be found here.

Integration guidelines for older services using play-ui can be found here

If you are not able to use the above libraries, please contact #team-plat-ui for integration advice.

How do I test the integration locally?

  1. Use service manager 2 to spin up tracking consent locally on port 12345:

    sm2 --start TRACKING_CONSENT_FRONTEND
    
  2. If you are not using CSPFilter, make the required modifications to your CSP policy (see below).

  3. Navigate to a page on your service. You should see the cookie banner at the top of the page, above the 'Skip to main content' link.

  4. Toggle to the Welsh language, if applicable. You should see the cookie banner translated into Welsh.

  5. Click 'Accept additional cookies', you should see a confirmation.

  6. Click 'Change your cookie settings'. You should see the cookie settings page.

  7. Click 'Save changes'. You should be scrolled to the top of the page and see a save confirmation.

Note on the back link when testing locally

When testing locally the 'Go back to the page you were looking at' link will either not display or will navigate to / which is likely to result in a 404 page not found error.

This is because:

  • when running locally, tracking consent is running on a different port
  • the back link uses document.referrer to identify the referring service url and the strict-origin-when-cross-origin referrer policy prevents the full path from being available

If you wish to satisfy yourself that the back link is operating correctly, you will need to deploy to an environment.

Content Security Policy (CSP)

Because tracking consent depends on GTM, your service will need to have a CSP that is compatible with it.

If you're following the current platform security Content Security Policy guidance, you do not need to make any changes to your CSP as it will be configured by the bootstrap-play library.

To find out more about what the latest guidance is, check out the #event-content-security-policy in the HMRC Digital slack.

How can I read and honour a user's cookie preferences?

Tracking consent stores cookie preferences in the userConsent cookie. Readable on the server and client side, the contents are URL-encoded JSON with a structure described in the next section. However, it‘s not intended that you inspect this cookie manually – helper methods have been created for reading it in Scala and Javascript.

Your service can be made aware of the contents of this cookie either by inspecting it on demand or, on the client-side only, subscribing to updates.

In Scala

For reading the cookie in server-side Scala, refer to the README in hmrc/tracking-consent-models

Your service will need to regularly update the version of this library in order to take advantage of changes to the underlying cookie model.

In Javascript

In Javascript, you can use the window.trackingConsent.UserPreferences object that has methods for querying the userConsent cookie and for subscribing to notifications for updates to it.

For example, if you need to determine if the user has accepted settings cookies,

if (window.trackingConsent.userPreferences.getPreferences().settings) {
  document.cookie = 'darkMode=true;path=/;Max-Age=31536000;';
}

If you want to perform an action only once the user has accepted settings cookies via the banner,

const communicator = {
  sendPreferences: (userPreferences, event) => {
    if (userPreferences.getPreferences().settings && event === 'CONSENT_UPDATED_EVENT') {
      document.cookie = 'textSize=large;path=/;Max-Age=31536000;';
    }
  }
}

window.trackingConsent.userPreferences.subscribe(communicator);

In the above example, event is set to either SERVICE_PAGE_LOAD_EVENT or CONSENT_UPDATED_EVENT. The SERVICE_PAGE_LOAD_EVENT is sent on the initial page load. The CONSENT_UPDATED_EVENT is sent when a user explicitly changes their settings via the cookie banner or the cookie settings page.

Cookie structure

Represented as a Typescript type, the structure of the URL-decoded userConsent cookie is as follows:

 {
  version: string, // 2021.1
  datetimeSet: string, // e.g. 2021-03-04T15:16:47.077Z
  preferences: {
    measurement?: boolean, // e.g. true
    settings?: boolean // e.g. false
  }
}

version

A string of the form YYYY.N, where YYYY is the year and N is a positive integer e.g. 2022.5.

The version number allows consuming services to know exactly which version of the user consent cookie was in use at the time the user consented. Any breaking changes to the descriptions of the cookie categories on the cookie settings page should result in a new version number as it affects what exactly the user has consented to.

datetimeSet

The date and time the cookie was set in UTC, ISO 8106 format e.g. 2021-03-04T15:16:47.077Z

preferences

A JSON object with the following optional keys:

  • measurement: when set to true, this indicates the user has consented to measurement cookies being stored on their device.
  • settings: when set to true, this indicates the user has consented to settings cookies being stored on their device.

Each preference setting should be interpreted as a strict boolean value. So-called ‘truthy’ values or even the literal string “true” should be interpreted as the user having rejected cookies.

In raw form the cookie value might look like:

{%22version%22:%222021.1%22%2C%22datetimeSet%22:%222021-03-10T13:48:19.321Z%22%2C%22preferences%22:{%22measurement%22:false%2C%22settings%22:false}}

This would result in the following URL-decoded value:

{"version":"2021.1","datetimeSet":"2021-03-10T13:48:19.321Z","preferences":{"measurement":false,"settings":false}}

Internal services and user tracking

This service is NOT intended for use on internal HMRC services which live on the internal domain. If you require user tracking for an internal service, please talk to your Performance Analyst to implement directly.

Integration with Google Tag Manager

Tracking consent frontend provides a standardised GTM integration that means service teams do not need to add a separate GTM snippet in addition to tracking-consent-frontend tracking.js snippet. Tracking consent uses the GTM datalayer to send messages to GTM in the form of events and variables that allows analytics tags to be configured such that they will only be added when a user has properly consented.

Datalayer Variables

On page load, tracking consent initialises the datalayer with the following variables:

Variable Description Possible values
trackingConsentLoaded Set to true if the service has integrated with tracking-consent-frontend true or undefined
trackingConsentMeasurementAccepted Set to true if the user has accepted measurement cookies or false otherwise true or false
trackingConsentSettingsAccepted Set to true if the user has accepted settings cookies or false otherwise true or false

When a user changes their cookie settings by accepting or rejecting cookies in the banner, or by toggling different settings on the cookie settings page, the variables trackingConsentMeasurementAccepted and trackingConsentSettingsAccepted are dynamically updated to reflect the user's adjusted settings.

Datalayer Events

GTM has a special data layer variable called an event that can be used to initiate tag firing when a user performs some action on a service page, such as clicking on a button.

Tracking consent sends the following events to GTM via the datalayer:

trackingConsentMeasurementAccepted is sent when

  • a user clicks 'Accept additional cookies'
  • clicks 'Save changes' on the cookie settings page having toggled the 'Use cookies that measure my website use' radio button.
  • on page load, if the user has already accepted measurement cookies on a visit to any other service on the tax platform using one of the mechanisms above

trackingConsentSettingsAccepted is sent when

  • a user clicks 'Accept additional cookies'
  • clicks 'Save changes' on the cookie settings page having toggled the 'Use cookies that remember my settings on services' radio button.
  • on page load, if the user has already accepted settings cookies on a visit to any other service on the tax platform using one of the mechanisms above

Internally this mechanism is implemented via gtmCommunicatorFactory. Messages are sent to GTM for both SERVICE_PAGE_LOAD_EVENT and CONSENT_UPDATED_EVENT events.

Integration with Optimizely

Optimizely is a client-side solution for A/B testing that relies on cookies. It supports a mechanism for opting a user out of cookie and local storage that is implemented by tracking consent. However, for this to work correctly it is imperative that:

  • tracking consent is loaded synchronously in the HEAD element – there must be no async attribute
  • tracking consent is loaded before the Optimizely snippet
  • the Optimizely snippet is wrapped in a conditional comment to prevent its inclusion on IE8 or IE9 browsers. Tracking consent does not support these browsers.

Due to the above complexities, it is highly recommended that consuming services using the Play framework use the tracking consent helpers provided in play-frontend-hmrc and play-ui

The following should be noted as consequences of the above:

  1. A/B testing will be disabled for users that have not accepted 'measurement' cookies before they navigated to the page under test.
  2. Users that accept additional cookies using the cookie banner will only see variations following a page reload or on subsequent pages in the user journey. They will not see the page change to a variation following acceptance.

Internally this mechanism is implemented via optimizelyCommunicatorFactory. An opt-out message is sent to Optimizely for both SERVICE_PAGE_LOAD_EVENT and CONSENT_UPDATED_EVENT events.

Optimizely integration with Google Analytics using Google Tag Manager

In order for analytics data relating to Optimizely experiments to be successfully communicated to Google Analytics, a special Universal Analytics GTM tag must be configured according to the instructions on the Optimizely website This configuration must be carried out once for each GTM container.

As a convenience, tracking consent hosts the custom integration code under the endpoint https://www.tax.service.gov.uk/tracking-consent/tracking/optimizely.js needed by Step 9 of the above integration guide. This means it is not necessary to carry out Step 9 - Custom HTML tag described in the above integration guide. The tracking consent helpers in hmrc/play-frontend-hmrc and hmrc/play-ui add this snippet after the optimizely snippet, to ensure the Optimizely object is available when it runs.

Integration with internal auditing

For auditing purposes, when cookie preferences are changed using the cookie banner or cookie settings page, tracking consent performs an XHR POST request to the /tracking-consent/audit endpoint with a payload containing the cookie preferences selected by the user. For example, clicking 'Accept additional cookies' results in a request similar to the following (with some headers removed):

POST /tracking-consent/audit HTTP/1.1
Host: www.tax.service.gov.uk
Csrf-Token: nocheck
Content-type: application/json
Origin: https://www.tax.service.gov.uk
Cookie: ...

{"measurement":true,"settings":true}

This request results in tracking consent sending an implicit audit event to the datastream microservice via the audit filter in bootstrap-play that internally uses the audit connector from play-auditing library.

Internally this mechanism is implemented via auditCommunicatorFactory, listening to events of type CONSENT_UPDATED_EVENT only.

What will happen when a user revokes previously granted tracking consent?

When a user grants tracking consent, GTM will be loaded on the page and that will create Google Analytics cookies on the tax.service.gov.uk domain. If a user then revokes their consent, these cookies will remain, but their tracking preference will be updated and on future navigation GTM will not be loaded and no tracking data will be sent. The Google analytics cookies will remain in the users browser until they expire, you can view further details about the nature of the cookies within the google analytics documenation on cookie usage.

Maintenance documentation

Maintenance documentation for the owning team, including architectural decision records (ADRs) can be found here.

License

This code is open source software licensed under the Apache 2.0 License.