Consent Management

Comprehensive guide to managing user consent for GDPR, COPPA, and other privacy regulations.

ℹ️ What is Consent Management? Consent management is the process of obtaining, storing, and respecting user preferences for data collection and personalized advertising. It's required by GDPR (EU), COPPA (US kids), and other privacy laws worldwide.

Why Consent Management Matters

  • Legal Compliance: Required by GDPR, COPPA, CCPA, and other laws
  • User Trust: Transparent consent builds trust with users
  • Better Monetization: Proper consent = more ad inventory = higher revenue
  • Platform Requirements: Google, Apple, and stores require it

Consent Management Flow

Flow Diagram
// Complete Consent Management Flow

1. App Launch
   ↓
2. Check if consent is required
   • EU users? → GDPR consent needed
   • Child user? → COPPA mode needed
   • Other regions? → Optional but recommended
   ↓
3. Check if consent already obtained
   • Yes → Use stored consent
   • No → Show consent dialog
   ↓
4. User makes choice
   • Accept → Personalized ads allowed
   • Decline → Contextual ads only
   • (For kids: Always contextual only)
   ↓
5. Store consent choice
   • localStorage or backend
   • Include timestamp
   • Allow user to change later
   ↓
6. Initialize SDK with consent
   • Pass consent flags to init()
   • SDK respects user choice
   ↓
7. Show ads accordingly
   • Personalized (if consented)
   • Contextual (if declined/child)

Implementation Options

Option 1: Simple Consent Dialog (DIY)

Build your own consent UI. Best for simple apps.

JavaScript
class SimpleConsentManager {
    constructor() {
        this.consentKey = 'user_consent';
    }
    
    async checkAndRequestConsent() {
        // Check if consent already given
        const stored = localStorage.getItem(this.consentKey);
        if (stored) {
            return JSON.parse(stored);
        }
        
        // Check if user needs consent (EU user)
        const needsConsent = await this.isEUUser();
        if (!needsConsent) {
            // Non-EU user, default to consented
            return { gdprApplies: false, gdprConsent: true };
        }
        
        // Show consent dialog
        const consent = await this.showConsentDialog();
        
        // Store consent
        localStorage.setItem(this.consentKey, JSON.stringify({
            gdprApplies: true,
            gdprConsent: consent,
            timestamp: Date.now()
        }));
        
        return { gdprApplies: true, gdprConsent: consent };
    }
    
    async isEUUser() {
        try {
            const res = await fetch('https://ipapi.co/json/');
            const data = await res.json();
            const euCountries = ['AT','BE','BG','HR','CY','CZ','DK','EE','FI','FR','DE','GR','HU','IE','IT','LV','LT','LU','MT','NL','PL','PT','RO','SK','SI','ES','SE','GB'];
            return euCountries.includes(data.country_code);
        } catch {
            return false;
        }
    }
    
    showConsentDialog() {
        return new Promise((resolve) => {
            // Create dialog
            const dialog = document.createElement('div');
            dialog.style.cssText = `
                position: fixed;
                bottom: 0;
                left: 0;
                right: 0;
                background: white;
                padding: 20px;
                box-shadow: 0 -2px 10px rgba(0,0,0,0.2);
                z-index: 9999;
            `;
            
            dialog.innerHTML = `
                

Your Privacy Matters

We use cookies and collect data to provide personalized ads. You can choose to accept or decline.


Privacy Policy `; document.body.appendChild(dialog); document.getElementById('accept-btn').onclick = () => { document.body.removeChild(dialog); resolve(true); }; document.getElementById('decline-btn').onclick = () => { document.body.removeChild(dialog); resolve(false); }; }); } clearConsent() { localStorage.removeItem(this.consentKey); } } // Usage const consentManager = new SimpleConsentManager(); async function initApp() { const consent = await consentManager.checkAndRequestConsent(); // Initialize SDK with consent RewardedAd.init({ appId: "YOUR_APP_ID", apiKey: "YOUR_API_KEY", userId: "user_123", gdprApplies: consent.gdprApplies, gdprConsent: consent.gdprConsent, onReward: function(reward) { grantCoins(100); } }); } initApp();

Option 2: Consent Management Platform (CMP)

Use a third-party CMP for compliance. Recommended for complex apps.

Popular CMPs:

  • Cookiebot: Easy integration, multi-language
  • OneTrust: Enterprise-grade, comprehensive
  • Termly: Free tier available, simple
  • Quantcast Choice: IAB TCF 2.0 compliant

Example: Cookiebot Integration

JavaScript
// Add Cookiebot script to your HTML
// 

// Wait for Cookiebot to load
window.addEventListener('CookiebotOnLoad', function() {
    const gdprApplies = Cookiebot.regulations.gdprApplies;
    const gdprConsent = Cookiebot.consent.marketing;
    
    // Initialize SDK with Cookiebot consent
    RewardedAd.init({
        appId: "YOUR_APP_ID",
        apiKey: "YOUR_API_KEY",
        userId: "user_123",
        gdprApplies: gdprApplies,
        gdprConsent: gdprConsent,
        onReward: function(reward) {
            grantCoins(100);
        }
    });
});

// Listen for consent changes
window.addEventListener('CookiebotOnAccept', function() {
    console.log('User accepted marketing consent');
    // Reinitialize SDK if needed
});

window.addEventListener('CookiebotOnDecline', function() {
    console.log('User declined marketing consent');
    // Switch to contextual ads only
});

Complete Consent Manager

Production-ready consent management with both GDPR and COPPA:

JavaScript
class AdvancedConsentManager {
    constructor() {
        this.consentKey = 'bzze_consent_data';
        this.consentData = this.loadConsent();
    }
    
    loadConsent() {
        const stored = localStorage.getItem(this.consentKey);
        if (stored) {
            return JSON.parse(stored);
        }
        return null;
    }
    
    saveConsent(data) {
        localStorage.setItem(this.consentKey, JSON.stringify({
            ...data,
            timestamp: Date.now(),
            version: '1.0'
        }));
        this.consentData = data;
    }
    
    async initialize() {
        // Check if we need to re-request consent (30 days expired)
        if (this.consentData) {
            const age = Date.now() - this.consentData.timestamp;
            const thirtyDays = 30 * 24 * 60 * 60 * 1000;
            if (age < thirtyDays) {
                return this.consentData;
            }
        }
        
        // Get user location and age
        const [isEU, isChild] = await Promise.all([
            this.checkEUUser(),
            this.checkChildUser()
        ]);
        
        let consent = {
            gdprApplies: isEU,
            gdprConsent: !isEU, // Default true for non-EU
            coppaCompliant: true,
            isChildDirected: isChild
        };
        
        // Request consent if needed
        if (isEU && !isChild) {
            consent.gdprConsent = await this.requestGDPRConsent();
        }
        
        if (isChild) {
            // Children always get contextual ads only
            consent.gdprConsent = false;
            consent.isChildDirected = true;
        }
        
        this.saveConsent(consent);
        return consent;
    }
    
    async checkEUUser() {
        try {
            const res = await fetch('https://ipapi.co/json/');
            const data = await res.json();
            const euCountries = ['AT','BE','BG','HR','CY','CZ','DK','EE','FI','FR','DE','GR','HU','IE','IT','LV','LT','LU','MT','NL','PL','PT','RO','SK','SI','ES','SE','GB','NO','IS','LI'];
            return euCountries.includes(data.country_code);
        } catch {
            return false;
        }
    }
    
    async checkChildUser() {
        const storedAge = localStorage.getItem('userAge');
        if (storedAge) {
            return parseInt(storedAge) < 13;
        }
        
        // Show age gate
        const birthYear = await this.showAgeGate();
        if (!birthYear) return false;
        
        const age = new Date().getFullYear() - birthYear;
        localStorage.setItem('userAge', age);
        return age < 13;
    }
    
    showAgeGate() {
        return new Promise((resolve) => {
            const dialog = document.createElement('div');
            dialog.style.cssText = `
                position: fixed;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                background: white;
                padding: 30px;
                border-radius: 10px;
                box-shadow: 0 4px 20px rgba(0,0,0,0.3);
                z-index: 10000;
                text-align: center;
            `;
            
            dialog.innerHTML = `
                

Welcome!

Please enter your birth year to continue:


`; const overlay = document.createElement('div'); overlay.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 9999; `; document.body.appendChild(overlay); document.body.appendChild(dialog); document.getElementById('submit-age-btn').onclick = () => { const year = parseInt(document.getElementById('birth-year-input').value); if (year && year >= 1900 && year <= new Date().getFullYear()) { document.body.removeChild(overlay); document.body.removeChild(dialog); resolve(year); } else { alert('Please enter a valid year'); } }; }); } requestGDPRConsent() { return new Promise((resolve) => { const dialog = document.createElement('div'); dialog.style.cssText = ` position: fixed; bottom: 0; left: 0; right: 0; background: white; padding: 20px; box-shadow: 0 -2px 10px rgba(0,0,0,0.2); z-index: 9999; font-family: Arial, sans-serif; `; dialog.innerHTML = `

🍪 Your Privacy Matters

We use cookies and collect data to provide personalized ads and improve your experience. You can choose to accept or decline personalized advertising.


Privacy Policy | Cookie Policy `; document.body.appendChild(dialog); document.getElementById('accept-all-btn').onclick = () => { document.body.removeChild(dialog); resolve(true); }; document.getElementById('decline-btn').onclick = () => { document.body.removeChild(dialog); resolve(false); }; }); } getConsent() { return this.consentData; } resetConsent() { localStorage.removeItem(this.consentKey); localStorage.removeItem('userAge'); this.consentData = null; } } // Usage const consentManager = new AdvancedConsentManager(); async function startApp() { const consent = await consentManager.initialize(); console.log('Consent obtained:', consent); // Initialize BZZE Ads SDK with consent RewardedAd.init({ appId: "YOUR_APP_ID", apiKey: "YOUR_API_KEY", userId: "user_" + Math.random().toString(36).substr(2, 9), // GDPR gdprApplies: consent.gdprApplies, gdprConsent: consent.gdprConsent, // COPPA coppaCompliant: consent.coppaCompliant, isChildDirected: consent.isChildDirected, onReward: function(reward) { console.log('Reward granted!'); grantCoins(100); }, onError: function(error) { console.error('Ad error:', error); } }); // Start your game startGame(); } // Initialize app startApp();

Consent Storage

What to Store

JavaScript
const consentData = {
    // GDPR
    gdprApplies: true,           // Is user in EU?
    gdprConsent: true,           // Did user consent?
    
    // COPPA
    coppaCompliant: true,        // Is app COPPA compliant?
    isChildDirected: false,      // Is user a child?
    
    // Metadata
    timestamp: 1234567890,       // When consent was given
    version: '1.0',              // Consent version
    consentString: 'base64...'   // IAB TCF string (if using)
};

Storage Options

Method Pros Cons
localStorage Simple, fast, client-side Cleared if user clears browser data
Backend Database Persistent, auditable, secure Requires server, user account
CMP Service Fully managed, compliant External dependency, cost

Allowing Users to Change Consent

Users must be able to withdraw or modify consent at any time:

JavaScript
// Add "Privacy Settings" button to your app
function showPrivacySettings() {
    const consent = consentManager.getConsent();
    
    const dialog = `
        

Privacy Settings

Personalized ads are based on your activity and help support free content.

`; // Show dialog document.body.insertAdjacentHTML('beforeend', dialog); } function savePrivacySettings() { const newConsent = document.getElementById('personalized-ads').checked; // Update consent consentManager.saveConsent({ ...consentManager.getConsent(), gdprConsent: newConsent }); // Notify user alert('Privacy settings updated!'); // Close dialog closePrivacySettings(); // Optionally: Reload app with new settings location.reload(); }

Best Practices

✅ DO:
  • Request consent before loading any ads or trackers
  • Use clear, simple language in consent dialogs
  • Store consent timestamp and version
  • Allow users to change consent anytime
  • Re-request consent every 30 days (recommended)
  • Test consent flow thoroughly
  • Document your consent process
⚠️ DON'T:
  • Pre-check consent boxes
  • Make consent mandatory for app access (unless legal basis)
  • Hide consent options in complex menus
  • Use dark patterns to trick users into consenting
  • Ignore consent choices
  • Forget to handle consent withdrawal

Testing Your Consent Flow

JavaScript
// Testing checklist
function testConsentFlow() {
    console.log('🧪 Testing Consent Flow...');
    
    // 1. Clear existing consent
    consentManager.resetConsent();
    console.log('✅ Consent cleared');
    
    // 2. Reload page to trigger consent flow
    // (Manually test consent dialog appearance)
    
    // 3. Test both Accept and Decline paths
    // (Manually test both options)
    
    // 4. Verify consent is stored
    const consent = consentManager.getConsent();
    console.log('Stored consent:', consent);
    
    // 5. Verify SDK receives correct flags
    const info = RewardedAd.getInfo();
    console.log('SDK config:', info.config);
    
    // 6. Test consent withdrawal
    // (Add "Privacy Settings" button to UI)
    
    console.log('✅ Consent flow test complete');
}

// Run test
testConsentFlow();

Related Topics

Resources