selli/laravel-gdpr-consent-database

A simple consent database for laravel applications to manage GDPR consents

Maintainers

Package info

github.com/Sellinnate/laravel-gdpr-consent-database

Homepage

Issues

pkg:composer/selli/laravel-gdpr-consent-database

Fund package maintenance!

selli

Statistics

Installs: 1 311

Dependents: 0

Suggesters: 0

Stars: 18


README

Laravel GDPR Consent Manager — Consent & privacy for Laravel

Laravel GDPR Consent Database

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

A Laravel package that gives you the consent backbone for GDPR compliance: typed and versioned consents, expiration & renewal, guest (cookie-based) consents, an immutable audit trail (your Art. 7(1) proof), erasure by pseudonymisation (Art. 17), data-subject export (Art. 15/20) and a configurable cookie banner.

⚖️ Scope & honesty: this package is a strong consent-management layer — not a turnkey "make me compliant" button, and not legal advice. It records cookie choices but does not itself block third-party scripts (ePrivacy script-gating is your job), has no children's-age mechanism, and audit-log immutability is enforced at the application level (not the database). Read Scope & limitations before relying on it, and validate your processing with your DPO.

📚 Full documentation: the complete, junior-proof guide (with the GDPR reasoning behind every feature) lives at laravel-gdpr-consent.selli.io (sources under docs/). This README is a quick reference.

Enterprise features

  • Typed, versioned consents with stable slugs — detect exactly who must re-consent after a policy change.
  • Immutable audit trail (consent_audit_logs) — proof of consent for GDPR Art. 7(1), with policy snapshot.
  • Expiration & renewal — time-limited consents and re-consent reminders.
  • Guest consents — session/cookie-based tracking for anonymous visitors.
  • Right to erasureanonymizeConsents() / gdpr:anonymize-subject pseudonymise without losing proof.
  • Art. 30 record-keepinglegal_basis, purpose, data_controller per consent type.
  • Configurable cookie banner with AJAX accept / reject / save-preferences endpoints.

Installation

You can install the package via composer:

composer require selli/laravel-gdpr-consent-database

Consent Categories

The package supports categorizing consent types to handle different types of consents appropriately:

  • cookie - For cookie-related consents (technical, profiling, tracking, etc.)
  • other - For non-cookie consents (marketing, newsletters, etc.)

Cookie Banner Integration

The cookie banner automatically filters and displays only consent types with category 'cookie'. Other consent types should be managed through your application's registration/preference flows.

You can publish and run the migrations with:

php artisan vendor:publish --tag="gdpr-consent-database-migrations"
php artisan migrate

You can publish the config file with:

php artisan vendor:publish --tag="gdpr-consent-database-config"

This is the contents of the published config file:

return [
    'text' => [
        'title' => 'Cookie Consent',
        'message' => 'We use cookies to enhance your browsing experience and analyze our traffic. By clicking "Accept All", you consent to our use of cookies.',
        'accept_text' => 'Accept All',
        'reject_text' => 'Reject All',
        'details_text' => 'Cookie Details',
        'back_text' => 'Back',
        'save_text' => 'Save Preferences',
        'icon_text' => 'Cookie Settings',
        'details_header' => 'Cookie Categories',
        'required_text' => '(Required)',
    ],

    'colors' => [
        'banner_background' => '#fff',
        'banner_border' => '#ddd',
        'banner_shadow' => 'rgba(0,0,0,0.1)',
        'text_primary' => '#333',
        'text_secondary' => '#666',
        'button_primary_bg' => '#007cba',
        'button_primary_hover' => '#005a87',
        'button_secondary_bg' => '#f1f1f1',
        'button_secondary_hover' => '#e1e1e1',
        'details_border' => '#eee',
    ],

    'icon' => [
        'position' => 'right',
        'display' => 'icon-with-text',
        'background' => '#007cba',
        'background_hover' => '#005a87',
    ],
];

Optionally, you can publish the views using

php artisan vendor:publish --tag="gdpr-consent-database-views"

Usage

Setup

After installing the package and running the migrations, you need to add the HasGdprConsents trait to your User model or any other model that needs to manage GDPR consents:

use Selli\LaravelGdprConsentDatabase\Traits\HasGdprConsents;

class User extends Authenticatable
{
    use HasGdprConsents;
    
    // ... rest of your model
}

Creating Consent Types

First, you need to create consent types that users can agree to:

use Selli\LaravelGdprConsentDatabase\Models\ConsentType;

// Create a required consent type (e.g., Terms and Conditions)
ConsentType::create([
    'name' => 'Terms and Conditions',
    'slug' => 'terms',
    'description' => 'Agreement to the website terms and conditions',
    'required' => true,
    'active' => true,
    'category' => 'other',
]);

// Create cookie-related consent
ConsentType::create([
    'name' => 'Technical Cookies',
    'slug' => 'technical-cookies',
    'description' => 'Essential cookies required for website functionality',
    'required' => true,
    'active' => true,
    'category' => 'cookie',
]);

// Create non-cookie consent
ConsentType::create([
    'name' => 'Marketing Emails',
    'slug' => 'marketing-emails', 
    'description' => 'Consent to receive marketing communications',
    'required' => false,
    'active' => true,
    'category' => 'other',
]);

Managing User Consents

Once you have set up consent types, you can manage user consents:

// Get the authenticated user
$user = auth()->user();

// Check if the user has given a specific consent
if ($user->hasConsent('marketing-emails')) {
    // User has consented to marketing emails
}

// Give consent (can use slug or consent type ID)
$user->giveConsent('marketing-emails');

// Give consent with additional metadata
$user->giveConsent('marketing-emails', [
    'source' => 'registration_form',
    'version' => '1.0',
]);

// Revoke consent
$user->revokeConsent('marketing-emails');

// Get all active consents for the user
$activeConsents = $user->activeConsents();

// Check if the user has all required consents
if ($user->hasAllRequiredConsents()) {
    // User has all required consents
} else {
    // Get missing required consents
    $missingConsents = $user->getMissingRequiredConsents();
}

Consent Versioning

Overview

The package supports versioning of consent types, which is essential for GDPR compliance when terms or policies change.

Usage

// Create a consent type with version
$consentType = ConsentType::create([
    'name' => 'Privacy Policy',
    'slug' => 'privacy-policy',
    'description' => 'Privacy Policy consent',
    'required' => true,
    'active' => true,
    'version' => '1.0',
]);

// Create a new version when terms change
$newVersion = $consentType->createNewVersion([
    'description' => 'Updated Privacy Policy consent',
]);

// Check if a user's consent is for the current version
if ($user->hasConsent('privacy-policy', true)) {
    // User has consented to the current version
} else {
    // User needs to renew consent for the new version
}

// Get all consents that need renewal (expired or outdated version)
$consentsNeedingRenewal = $user->consentsNeedingRenewal();

// Renew a consent with the latest version
$user->renewConsent('privacy-policy');

Consent Expiration

Overview

The package also supports consent expiration, allowing you to set validity periods for consents.

Usage

// Create a consent type with a validity period (in months)
ConsentType::create([
    'name' => 'Marketing Emails',
    'slug' => 'marketing-emails',
    'description' => 'Consent to receive marketing emails',
    'required' => false,
    'active' => true,
    'category' => 'other',
    'version' => '1.0',
    'validity_months' => 12, // Consent valid for 12 months
]);

// Give consent with a custom validity period
$user->giveConsent('marketing-emails', [], 6); // Valid for 6 months

// Check if a consent is expired
$consent = $user->consents()->where('consent_type_id', $consentTypeId)->first();
if ($consent->isExpired()) {
    // Consent is expired
}

// Get all expired consents
$expiredConsents = $user->expiredConsents();

// Get consents that are about to expire within the next 30 days
$soonExpiringConsents = $user->getConsentsExpiringWithinDays(30);

Guest Consent Management

For non-logged-in users, the package provides session-based consent management using technical cookie codes:

use Selli\LaravelGdprConsentDatabase\Services\GuestConsentManager;

$guestManager = new GuestConsentManager();

// Give consent for a guest user (uses current session)
$guestManager->giveConsent('marketing-emails', [
    'source' => 'cookie_banner',
]);

// Check if guest has given consent
if ($guestManager->hasConsent('marketing-emails')) {
    // Guest has consented to marketing emails
}

// Get all active consents for guest
$activeConsents = $guestManager->getActiveConsents();

// Check if guest has all required consents
if ($guestManager->hasAllRequiredConsents()) {
    // Guest has all required consents
}

// Work with specific technical cookie code (session ID)
$technicalCookieCode = 'gdpr_abc123_1234567890';
$guestManager->giveConsent('terms', [], null, $technicalCookieCode);

// Revoke guest consent
$guestManager->revokeConsent('marketing-emails', $technicalCookieCode);

The guest consent system uses the guest_consents table to track session information and links to user_consents using the technical cookie code as the session identifier.

Using the Cookie Consent Seeder

Run the seeder to populate default cookie consent types:

php artisan db:seed --class="Selli\LaravelGdprConsentDatabase\Database\Seeders\CookieConsentSeeder"

Cookie Banner Integration

The package provides a blade directive for displaying cookie consent banners:

{{-- Basic usage --}}
@gdprCookieBanner

{{-- With custom options --}}
@gdprCookieBanner([
    'title' => 'Cookie Preferences',
    'message' => 'We use cookies to improve your experience.',
    'acceptText' => 'Accept All Cookies',
    'rejectText' => 'Reject Optional',
    'consentTypes' => $consentTypes
])

Publishing Views

You can publish and customize the cookie banner view:

php artisan vendor:publish --tag="gdpr-consent-database-views"

This will publish the view to resources/views/vendor/gdpr-consent-database/cookie-banner.blade.php.

Customization Options

The cookie banner supports extensive customization through the config file:

  • Text customization: All button labels, messages, and UI text can be customized
  • Color theming: Complete color scheme customization including backgrounds, borders, and button colors
  • Icon positioning: Configure the consent settings icon position (right, left, top, bottom)
  • Icon display: Choose between icon-only or icon-with-text display modes
  • Responsive design: Automatic mobile-friendly adaptations

JavaScript Integration

The cookie banner includes built-in JavaScript for handling user interactions. It automatically:

  • Shows the banner for users who haven't given consent
  • Handles accept/reject actions via AJAX
  • Stores consent state using cookies (gdpr_consent_given, gdpr_session_id)
  • Provides detailed consent management with expandable categories
  • Shows a consent settings icon after initial consent is given
  • Manages technical cookie codes for session-based guest consent tracking
  • Automatically checks consent status on page load and shows appropriate UI

Routes

The package automatically registers these routes for consent management:

  • POST /gdpr/consent/accept-all - Accept all consent types
  • POST /gdpr/consent/reject-all - Accept only required consents
  • POST /gdpr/consent/save-preferences - Save specific consent preferences
  • POST /gdpr/consent/status - Get current consent status for the session

Make sure to include the CSRF token in your layout:

<meta name="csrf-token" content="{{ csrf_token() }}">

Database Structure

This package creates four tables. See the database schema reference for the full breakdown.

consent_types

Stores the different types of consent that can be requested from users:

  • id: Primary key
  • name: Name of the consent type
  • slug: Stable group identifier (not unique on its own — one row per version; see Versioning)
  • description: Detailed description of what the user is consenting to
  • required: Boolean indicating if this consent is required
  • active: Boolean indicating if this is the current (active) version of the group
  • version: Version string for consent type versioning (default: '1.0')
  • validity_months: Number of months the consent remains valid (nullable)
  • effective_from / effective_until: Effective time window (nullable)
  • category: Category of consent ('cookie' or 'other', default: 'other')
  • legal_basis / purpose / data_controller: GDPR Art. 30 record-keeping (nullable)
  • policy_url / policy_text_hash: Snapshot of the policy shown for this version (nullable)
  • metadata: JSON field for additional data
  • timestamps: Created and updated timestamps

user_consents

Stores the actual user consents:

  • id: Primary key
  • consentable_id and consentable_type: Polymorphic relationship to the user model
  • consent_type_id: Foreign key to the consent type
  • consent_version: Version of the consent type when consent was given (nullable)
  • granted: Boolean indicating if consent is currently granted
  • granted_at: Timestamp when consent was granted
  • revoked_at: Timestamp when consent was revoked (if applicable)
  • expires_at: Timestamp when consent expires (nullable)
  • ip_address: IP address from which consent was given
  • user_agent: User agent from which consent was given
  • metadata: JSON field for additional data
  • timestamps: Created and updated timestamps

guest_consents

Stores guest user information for session-based consent tracking:

  • session_id: Primary key (technical cookie code/session ID)
  • ip_address: IP address of the guest user
  • user_agent: User agent of the guest user
  • metadata: JSON field for additional data
  • timestamps: Created and updated timestamps

This table works in conjunction with user_consents where guest consents are stored using the technical cookie code as the consentable_id with consentable_type set to the guest consent model.

consent_audit_logs

An append-only, immutable record of every consent action (your GDPR Art. 7(1) proof):

  • consentable_type / consentable_id: Polymorphic subject
  • consent_type_id: FK to the consent type (nullOnDelete — proof survives type deletion)
  • consent_type_slug / consent_version: Snapshot of what was consented to
  • action: granted, revoked, renewed, expired or anonymized
  • occurred_at: When the action happened
  • ip_address / user_agent: Request context (honours IP anonymization config)
  • policy_url / policy_text_hash: Snapshot of the policy shown
  • metadata: Additional data (grant/renew only)

Records cannot be updated or deleted through Eloquent. See the audit trail docs.

Extending the Package

Custom Consent Types

You can extend the ConsentType model to add custom functionality:

use Selli\LaravelGdprConsentDatabase\Models\ConsentType;

class MyConsentType extends ConsentType
{
    // Add custom methods or override existing ones
    
    public function isLegallyRequired()
    {
        return $this->required && isset($this->metadata['legal_basis']);
    }
}

Custom Consent Workflows

You can create custom consent workflows by extending the HasGdprConsents trait or by creating service classes that use the provided models:

use Selli\LaravelGdprConsentDatabase\Models\ConsentType;

class ConsentService
{
    public function processRegistrationConsents(User $user, array $consentData)
    {
        // Process multiple consents at once
        foreach ($consentData as $slug => $isGranted) {
            if ($isGranted) {
                $user->giveConsent($slug, ['source' => 'registration']);
            }
        }
        
        // Check if all required consents are given
        return $user->hasAllRequiredConsents();
    }
}

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.