orestbida / cookieconsent

:cookie: Simple cross-browser cookie-consent plugin written in vanilla js
https://playground.cookieconsent.orestbida.com/
MIT License
4.05k stars 416 forks source link

Add native support of GTM's Consent Mode (GCM) #110

Open jelen07 opened 2 years ago

jelen07 commented 2 years ago

Feature request

Add native support of Consent Mode for GTM like CookieHub, etc.

Snímek obrazovky 2021-11-21 v 8 45 10

Screenshot from CookieHub's administration that affect their plugin behaviour, which in case of this widget could be done by configuration.

Proposed solution

Add new configuration key ie. gtm_consent_mode: true|false (default) which will be automaticaly sending consents to the GTM. It's Google native support of what does the data-cookiecategory do. It could be used together.

Usefull links

  1. https://support.cookiehub.com/article/79-google-consent-mode-gcm
  2. https://support.cookiehub.com/article/136-consent-mode-in-google-tag-manager
  3. https://developers.google.com/tag-platform/tag-manager/templates/consent-apis
Snímek obrazovky 2021-11-21 v 8 44 05

Screenshot from GTM tag's setting.

orestbida commented 2 years ago

I've considered myself adding gtm internally before, since many use it. The only issue is that the plugin doesn't come with predefined categories and I can't know if the "analytics" (can be also translated in other languages) category is enabled or not.

This feature is something which could be implemented in a future v3 (I foresee plenty of breaking changes)

jelen07 commented 2 years ago

Do have some example of integration with GCM? I'm trying to bind plugins onAccept and onChange functions with GCM but without success yet.

orestbida commented 2 years ago

This is probably the easiest way:

 <script>
   // Define dataLayer and the gtag function.
   window.dataLayer = window.dataLayer || [];
   function gtag(){dataLayer.push(arguments);}

   // Default ad_storage to 'denied'.
   gtag('consent', 'default', {
     'ad_storage': 'denied',
     'analytics_storage': 'denied'
   });
 </script>

 <!-- Google Tag Manager -->
 <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
 new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
 j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
 })(window,document,'script','dataLayer','GTM-XXXXXX');</script>
 <!-- End Google Tag Manager -->

<!--- enable analytics when "analytics" category is selected --->
<script type="text/plain" data-cookiecategory="analytics">
    gtag('consent', 'update', {
        'analytics_storage': 'granted'
    });
</script>

<!--- enable ads when "ads" category is selected --->
<script type="text/plain" data-cookiecategory="ads">
    gtag('consent', 'update', {
        'ad_storage': 'granted'
    });
</script>

If you really need to enable gtag inside onAccept or onChange then something like this should work:

// ...
onAccept: function(){
    if(cc.allowedCategory('analytics')){
        gtag('consent', 'update', {
            'analytics_storage': 'granted'
        });
    }

    if(cc.allowedCategory('ads')){
        gtag('consent', 'update', {
            'ad_storage': 'granted'
        });
    }
}
// ...

you'd repeat the same code inside the onChange method. GTM code found here.

jelen07 commented 2 years ago

Well, and what setup you're using in GTM - native consent mode or some other solution? What trigger is used?

GCM
orestbida commented 2 years ago

Mhm, I'm using Google Tag Manager configured with Google Analytics: GA4. ad_storage and analytics_storage should already be handled automatically without further configuration.

screen

(I updated the snippet above 'cause I had mixed the GTM code GTM-XXXXXXX with gtag.js snippet.)

coli21 commented 2 years ago

@jelen07 @orestbida Did it work for you? Everything works fine with analytics but my Facebook Pixel does not load. "ad_storage" is set as condition in "Consent Settings" in GTM.

orestbida commented 2 years ago

@coli21 ,

hmm, I can't personally test facebook pixel's script, but I have tried with microsoft clarity — which also must be configured using the "custom html" option inside google tag manager — and it works properly.

Can you make sure that the "firing trigger" is set on "all pages"? Also, are you perhaps using "facebook pixel's consent mode" ?

orestbida commented 2 years ago

@coli21 ,

if you can't make it work, here's another way to fire facebook pixel's script, and that is by creating your own custom event and trigger!

To create a custom trigger: tag manager -> workspace -> triggers -> new -> trigger configuration -> choose trigger type -> choose "custom event" -> specify event name, e.g. "start_pixel" -> trigger fires on -> all custom events and save the trigger.

Now head over to facebook pixel's tag: Consent Settings (BETA) -> Require additional consent for tag to fire -> specify the custom event name, e.g. "start_pixel". On the trigger section below select only the newly created trigger.

Make sure to submit/publish changes!

The last piece of the puzzle is to manually send the custom event to google tag manager using dataLayer.push() like so:

<!--- enable ads when "ads" category is selected --->
<script type="text/plain" data-cookiecategory="ads">

    gtag('consent', 'update', {
        'ad_storage': 'granted'
    });

    // also enable facebook pixel
    dataLayer.push({'event': 'start_pixel'});
</script>
coli21 commented 2 years ago

@orestbida Thanks for your answers. This is my FB Pixel configuration inside Tag Manager. The code on the front end is the same one you used. This way does not word: when ads are accepted, the only cookie set is _gcl_au, but no FB pixel. What am I missing?

I'd like a "native" solution inside tag manager, without creating my own events or triggers.

Pixel GTM Configuration

orestbida commented 2 years ago

@coli21 ,

My bad! You don't NECESSARILY need to create a custom event.

You DO need the custom trigger, and the reason for that - based on my guess - is that unlike google's scripts which are well integrated with their own tools, third party scripts are not (yet)!

As I see it, It should be gtm's job to enable the custom scripts but it does not do that currently - perhaps it has to do with the "beta" flag next to "Consent Settings".

If facebook pixel's script only needs ad_storage consent to run, then you need to trigger the event manually using the dataLayer.push() method!

You need a custom trigger (active listener which will enable the script whenever it receives the custom event ad_storage) dss

This applies to all third party scripts configured with the custom html option inside google tag manager.

<!--- enable ads when "ads" category is selected --->
<script type="text/plain" data-cookiecategory="ads">

    gtag('consent', 'update', {
        'ad_storage': 'granted'
    });

    // send manual event to enable all third party scripts — configured with gtm — which rely on `ad_storage` consent
    dataLayer.push({'event': 'ad_storage'});
</script>

The last option would be to configure facebook pixel using a community template ddsds

Here's a more detailed tutorial regarding the "community template" configuration: How To Set Up The Facebook Pixel With Google Tag Manager

coli21 commented 2 years ago

@orestbida Thank you, now I got it. In fact, now that I think about it, my Analytics code works fine with analytics_storage. I hope there will be a better integration for third-party scripts in the future.

In the meantime, I found another solution: I removed "ad_storage" from Pixel Configuration and I inserted the script as plain text. This way, the ad_storage activation on the frontend is no longer needed.

FB Pixel new Configuration

jelen07 commented 2 years ago

@coli21 Check out the "Additional Consent Check" part. They (CookieHub) triggering cookiehub_consent_update event from their script amog with consent update.

Snímek obrazovky 2021-12-07 v 7 23 18

But how you can use custom trigger with other triggers? 😳

Snímek obrazovky 2021-12-07 v 7 25 38
orestbida commented 2 years ago

@jelen07,

a step-by-step guide:

https://user-images.githubusercontent.com/34487268/145094856-452bb257-1ea0-45df-b704-c7571702e106.mp4

jelen07 commented 2 years ago

@orestbida Yeah, that's pretty straightforward. But we're are trying to implement cookie consent only within the GTM - without additional implementation on the target websites. We will manage more than 500+ websites so additional changes in implementation will be highly time-consuming, which we would like to avoid. Because of this, we've made a wrapper https://github.com/68publishers/cookie-consent, which basically consists of two parts - the wrapper itself and the GTM template. It gives us a space to add some extra functionalities - default translations, default settings, and so on. All that stuff must be possibly overridden from GTM. We've done some pretty progress. But atm the setup requires extra triggers. If some "GTM pro user" have any hint, how to fire tags right after the user gives consent without custom triggers, just with "native GTM's consents", it would be great 😅

orestbida commented 2 years ago

@jelen07

I don't see how this can be achieved without any work on the google tag manager side. It would be great if gtm extended its "native" behaviour to custom scripts too, but that does not work; perhaps things will change when the beta label goes away.

Currently, the only solution to this — as far as I know of — would be to create a community template for google tag manager.

The template would then be able to read events sent from dataLayer.push() and enable scripts accordingly (scripts have to be configured with the template/plugin's custom events).

zdenekhatak commented 2 years ago

@jelen07 @orestbida

How about using https://www.simoahava.com/analytics/consent-settings-google-tag-manager/#listen-for-changes-in-consent-state listeners for consent changes?

Creating a bunch of gtm templates for various tags may do the trick and keep the logic separated.

Mangatt commented 2 years ago

@orestbida Your proposed solution looks nice, but I'm not sure how well it works after user opens settings again and deny previously granted consent. Do you think that this is enough or is it necessary to use callbacks?

It might be worth (and not that hard imo) to enable something like this:

<script type="text/plain" data-cookiecategory-deny="analytics">gtag("consent","update",{"analytics_storage":"denied"});</script>

What do you think?

nungr commented 2 years ago

If someone opens settings and denies consent – cookies are deleted, but GTM sends cookies again on page reload. Quick solution for "analytics" ( cookieconsent-init.js):

onChange: function (cookie, changed_preferences) {   
        var len = changed_preferences.length;
        for (var i = 0; i < len; i++) {
            let name = changed_preferences[i];
            if (cookie.level.includes(name)) {
                if (name == "analytics") {
                    gtag('consent', 'update', {
                        'analytics_storage': 'granted'
                    });
                }
            } else {
                if (name == "analytics") {
                    gtag('consent', 'update', {
                        'analytics_storage': 'denied'
                    });
                }
            }
        }
    },
AlkoKod commented 2 years ago

For Facebook pixel use Facebook consent mode: fbq('consent', 'revoke'); and fbq('consent', 'grant;

Just edit your pixel's code in GTM to have consent setting before init:

<script>
  !function(f,b,e,v,n,t,s)
  {if(f.fbq)return;n=f.fbq=function(){n.callMethod?
  n.callMethod.apply(n,arguments):n.queue.push(arguments)};
  if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
  n.queue=[];t=b.createElement(e);t.async=!0;
  t.src=v;s=b.getElementsByTagName(e)[0];
  s.parentNode.insertBefore(t,s)}(window, document,'script',
  'https://connect.facebook.net/en_US/fbevents.js');
  fbq('consent', 'revoke');
  fbq('init', 'PIXEL_ID');
  fbq('track', 'PageView');
</script>

And then call it on accept and on change functions like this:

onAccept: function (cookie) {
                    if (cc.allowedCategory('analytics')) {
                        gtag('consent', 'update', {
                            'analytics_storage': 'granted'
                        });
                        fbq('consent', 'grant');
                    }},

The _fbp cookie is still there after revoking the consent, but it seems the pixel script is not running. So best solution for me yet.

antonijo01 commented 1 year ago

I found step by step instructions how to implement Orestibda V2.9. with google consent mode: https://uninterrupted.tech/blog/manage-user-cookie-consent-with-google-tag-manager-a-step-by-step-guide/ What would be the simplest way to implement something similar for version 3.0? Is there a step by step tutorial somewhere?

JoshuaCrewe commented 1 year ago

@antonijo01 that has been one of the most useful articles I have ever read. Thank you so much for sharing!

reklamasieciowa commented 11 months ago

Hi, your plugin looks very nice. Before I came across it, I wrote my own one, but for WordPress.

I thought for a long time how to combine it with GTM, because without it there is no full Consent Mode. IMO Template for GTM works best.

I adapted the example given in the Google documentation to read consents from my cookie. You can easily and quickly adapt it to the structure of your cookie. Thanks to this, implementation in GTM is trivial: import the script template into GTM, add default settings (or change them), set the trigger to consent init, done.

My plugin also sends the consent update event, but adding it in your script takes a few minutes (onFirstConsent + onChange).

Would you like to talk about the possibility of implementing such a solution?

You probably know, there is time until March 2024 to implement Consent Mode V2 (two additional categories of consent).

everyx commented 7 months ago

A GTM template for reference: https://github.com/68publishers/cookie-consent

alxndr-w commented 5 months ago

Hi, I'm a little bit late to the party and NOT using GTM. But as I develop a Consent Management plugin for REDAXO CMS using cookieconsent + iframemanager, I do want to support GCM v2.

So basically, if I'm correct, there are three important things that need to be solved:

  1. The ability to organize 3rd-party scripts and/or groups in the GCM given categories security_storage, ad_storage, analytics_storage, ...
  2. The ability to not only to track consent but to revoke it, as well as
  3. The ability to sync revoke with cookieconsent and GCM v2's which keeps this information seperate from cookieconsent in their predefined localStorage object.

Native support by cookieconsent should be possible if there's an attribute/property per service, which categories are affected (1.), an additional callback event, e.g. onRevoke() instead of only onChange() (2.) or an internal method of cookieconsent that already does the job (3.) based on the information stored per service in (1.).

Or is there something I'm missing? E.g. why there is a solution by @everyx with a complex template for GTM? Why should I integrate cookieconsent into GTM if I could integrate GCM into the configuration of cookieconsent?