elegantly/laravel-cookies-consent

Cookie consent for Laravel

v1.0.5 2024-09-24 13:34 UTC

This package is auto-updated.

Last update: 2024-09-24 14:24:56 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

laravel-cookies-consent

This package provides a simple yet extremely flexible way to manage cookie consent in your Laravel application.

The default cookie banner design requires Tailwind CSS and Alpine.js, but you can publish the component and customize it with your own stack.

Demo here

Requirements

Backend

  • Laravel

Frontend

The default cookie consent banner included in this package requires:

Installation

You can install the package via Composer:

composer require elegantly/laravel-cookies-consent

You can publish the config file with:

php artisan vendor:publish --tag="cookies-consent-config"

This is the content of the published config file:

return [

    /*
    |--------------------------------------------------------------------------
    | URL Configuration
    |--------------------------------------------------------------------------
    |
    | These values determine the package's API route URLs. Both values are
    | nullable and represent the same concepts as Laravel's routing parameters.
    |
    */

    'url' => [
        'domain' => null,
        'prefix' => 'cookiesconsent',
    ],

    /*
    |--------------------------------------------------------------------------
    | Consent Cookie Configuration
    |--------------------------------------------------------------------------
    |
    | To keep track of the user's preferences, this package stores
    | an anonymized cookie. You do not need to register this cookie in the
    | package's cookie manager as it is done automatically (under "essentials").
    |
    | The duration parameter represents the cookie's lifetime in minutes.
    |
    | The domain parameter, when defined, determines the cookie's activity domain.
    | For multiple sub-domains, prefix your domain with "." (e.g., ".mydomain.com").
    |
    */

    'cookie' => [
        'name' => Str::slug(env('APP_NAME', 'laravel'), '_').'_cookiesconsent',
        'lifetime' => 60 * 24 * 365,
        'domain' => null,
    ],

    /*
    |--------------------------------------------------------------------------
    | Legal Page Configuration
    |--------------------------------------------------------------------------
    |
    | Most cookie notices display a link to a dedicated page explaining
    | the extended cookies usage policy. If your application has such a page,
    | you can add its route name here.
    |
    */

    'policy' => null,

];

Usage

This package covers both backend and frontend cookie consent management.

You can choose to use the package only for backend capabilities or for both.

Backend Usage

In the backend, you will register the cookies and a callback associated with each of them. This callback will be a JavaScript script to run when the consent is granted.

Register Your Cookies

First, you should register all the cookies requiring user consent.

To manage cookies, the package provides a service accessible via the Facade: Elegantly\CookiesConsent\Facades\CookiesConsent.

Cookie registration should be done in middleware to access the app and request context. This also allows you to choose the routes relying on those cookies.

To register your cookies, create a new middleware App\Http\Middleware\RegisterCookiesConsent. In this middleware, call CookiesConsent::register to register groups of cookies.

  • Cookies are always registered in groups.
  • A cookie is defined by its name, lifetime, and an optional description.
  • A cookie group can be defined as required. Such cookies cannot be rejected by the user, which is useful for essential cookies like the session cookie.

For example, all cookies related to "Marketing" can be registered together:

namespace App\Http\Middleware;

use Carbon\CarbonInterval;
use Closure;
use Elegantly\CookiesConsent\CookieDefinition;
use Elegantly\CookiesConsent\CookieGroupDefinition;
use Elegantly\CookiesConsent\Facades\CookiesConsent;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class RegisterCookiesConsent
{
    public function handle(Request $request, Closure $next): Response
    {
        // Register cookies related to the Facebook pixel
        CookiesConsent::register(new CookieGroupDefinition(
            key: 'marketing',
            name: __('cookies-consent::cookies.marketing.name'),
            description: __('cookies-consent::cookies.marketing.description'),
            items: [
                new CookieDefinition(
                    name: '_fbc',
                    lifetime: CarbonInterval::years(2),
                    description: __('cookies-consent::cookies._fbc.description')
                ),
                new CookieDefinition(
                    name: '_fbp',
                    lifetime: CarbonInterval::years(3),
                    description: __('cookies-consent::cookies._fbp.description')
                ),
            ],
            onAccepted: function () {
                return <<<'JS'
                    if (typeof fbq === 'function') {
                        fbq('consent', 'grant');
                    }
                JS;
            },
        ));

        return $next($request);
    }
}

Registering Essential Cookies

The package provides a preset for essential cookies. Essential cookies are those that cannot be removed without compromising the application. By default, Laravel includes 2 essential cookies:

  • XSRF-TOKEN
  • Session cookie

This package adds a third one:

  • Consents (a cookie to store consents).

You can automatically register these three essential cookies using:

use Elegantly\CookiesConsent\Facades\CookiesConsent;

CookiesConsent::registerEssentials()
    ->register(
        // ... custom cookie definition
    )

Registering Cookie Callbacks

Using the onAccepted parameter, you can define the JavaScript code to execute when consent is granted to a specific cookie group.

In the previous example, we grant consent using the Facebook pixel.

use Elegantly\CookiesConsent\Facades\CookiesConsent;

CookiesConsent::register(new CookieGroupDefinition(
    // ...
    onAccepted: function () {
        return <<<'JS'
            // This JavaScript code will be executed when consent is granted
            if (typeof fbq === 'function') {
                fbq('consent', 'grant');
            }
        JS;
    },
));

Frontend Usage

Using the Default Cookie Banner

You can use the default cookie banner included with this package. It requires js-cookie, Alpine and tailwindcss.

js-cookie Requirement

The default banner implementation requires the js-cookie library to parse cookies in the browser.

Add it to your project using the CDN:

<script src="https://cdn.jsdelivr.net/npm/js-cookie@3/dist/js.cookie.min.js"></script>

Or see their documentation to install it via npm.

Alpine.js Requirement

The default banner implementation requires Alpine.js for reactivity. Ensure it is included in your page.

Simply put the banner component <x-cookies-consent::banner /> at the end of your HTML page, and you are ready to go!

    <!-- ... -->
    <x-cookies-consent::banner />
</body>

tailwindcss Requirement

The default banner is styled with tailwindcss. You should add the following paths to your tailwind config file:

export default {
    content: [
        // ...
        "./vendor/elegantly/laravel-cookies-consent/resources/views/**/*.blade.php",
    ],
    // ...
};

Customizing the Default Component

You can customize the default component by publishing the views:

php artisan vendor:publish --tag="cookies-consent-views"

Using a Custom Component

You can design your own frontend cookie banner.

To retrieve all the cookie definitions, simply call:

use Elegantly\CookiesConsent\Facades\CookiesConsent;

CookiesConsent::getDefinition();

Facebook Pixel Cookie Consent

The Facebook Pixel tracks users and conversions on the client side. Documentation available here.

This is the historic way to track conversions. Facebook & Meta now also provide a way to track your conversions directly from your backend. It is called "API conversions" and the documentation is available here.

This example will only cover the Facebook Pixel as the "API conversions" do not need cookie consent.

Example

The Pixel provides a built-in manager for consent. This example relies on this.

1. Revoke consent on load

Before calling fbq('init', ...) and immediately after the Pixel script, revoke the consent:

<!-- Facebook Pixel Code -->
<!-- prettier-ignore -->
<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');

  // Revoke consent before init
  fbq("consent", "revoke"); 
  
  // Then call your logic as usual
  fbq('init', '{your-pixel-id-goes-here}');
  fbq('track', 'PageView');
</script>
<!-- End Facebook Pixel Code -->

2. Grant consent

In your middleware, register a cookie group and call fbq('consent', 'grant') in the onAccepted callback. Every call to fbq done before the consent will be triggered after fbq('consent', 'grant') is called.

use Elegantly\CookiesConsent\Facades\CookiesConsent;
use Elegantly\CookiesConsent\CookieGroupDefinition;
use Elegantly\CookiesConsent\CookieDefinition;
use Carbon\CarbonInterval;

CookiesConsent::register(new CookieGroupDefinition(
    key: 'marketing', // customize this value if you want
    name: __('cookies-consent::cookies.marketing.name'), // customize this value if you want
    description: __('cookies-consent::cookies.marketing.description'), // customize this value if you want
    items: [
        new CookieDefinition(
            name: '_fbc',
            lifetime: CarbonInterval::years(2),
            description: __('cookies-consent::cookies._fbc.description')
        ),
        new CookieDefinition(
            name: '_fbp',
            lifetime: CarbonInterval::years(3),
            description: __('cookies-consent::cookies._fbp.description')
        ),
    ],
    onAccepted: function () {
        return <<<'JS'
                if(typeof fbq === 'function'){
                    fbq('consent', 'grant');
                }
            JS;
    },
));

References

Facebook Guide: General Data Protection Regulation

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.