A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1738 lines
48 KiB
Markdown
1738 lines
48 KiB
Markdown
# Cookie & Consent Banner - Technische Spezifikation
|
|
|
|
**Version:** 1.0.0
|
|
**Status:** Draft
|
|
**Lizenz:** Apache 2.0 (Open Source, kommerziell nutzbar)
|
|
**Letzte Aktualisierung:** 2026-02-04
|
|
|
|
---
|
|
|
|
## Inhaltsverzeichnis
|
|
|
|
1. [Executive Summary](#1-executive-summary)
|
|
2. [Rechtliche Anforderungen](#2-rechtliche-anforderungen)
|
|
3. [Architektur-Übersicht](#3-architektur-übersicht)
|
|
4. [Consent-Kategorien](#4-consent-kategorien)
|
|
5. [SDK-Spezifikation](#5-sdk-spezifikation)
|
|
6. [API-Spezifikation](#6-api-spezifikation)
|
|
7. [UI/UX-Anforderungen](#7-uiux-anforderungen)
|
|
8. [Plattform-Integrationen](#8-plattform-integrationen)
|
|
9. [Datenspeicherung & Audit](#9-datenspeicherung--audit)
|
|
10. [Sicherheit & Privacy](#10-sicherheit--privacy)
|
|
11. [Implementierungs-Roadmap](#11-implementierungs-roadmap)
|
|
12. [Anhang](#12-anhang)
|
|
|
|
---
|
|
|
|
## 1. Executive Summary
|
|
|
|
### 1.1 Projektziel
|
|
|
|
Entwicklung eines **plattformübergreifenden Consent-Management-Systems** (CMP), das:
|
|
|
|
- DSGVO, TTDSG und ePrivacy-Richtlinie vollständig erfüllt
|
|
- In PWAs, Websites, Mobile Apps und Desktop-Anwendungen integrierbar ist
|
|
- Als Open-Source-Lösung kommerziell nutzbar ist (Apache 2.0)
|
|
- Über SDKs für alle gängigen Plattformen verfügbar ist
|
|
- Mit dem bestehenden BreakPilot Consent-Service integriert
|
|
|
|
### 1.2 Kernfunktionen
|
|
|
|
| Funktion | Beschreibung |
|
|
|----------|--------------|
|
|
| **Consent Collection** | Granulare Einwilligung pro Kategorie und Anbieter |
|
|
| **Consent Storage** | Revisionssichere Speicherung aller Einwilligungen |
|
|
| **Consent Withdrawal** | Jederzeitiger Widerruf mit gleicher Einfachheit |
|
|
| **Cross-Platform Sync** | Consent-Status über alle Plattformen synchronisiert |
|
|
| **TCF 2.2 Support** | IAB Transparency & Consent Framework Integration |
|
|
| **A/B Testing** | DSGVO-konforme Banner-Optimierung |
|
|
| **Analytics** | Anonymisierte Consent-Statistiken |
|
|
|
|
### 1.3 Zielplattformen
|
|
|
|
- **Web:** JavaScript SDK (Vanilla, React, Vue, Angular, Svelte)
|
|
- **PWA:** Service Worker Integration mit Offline-Support
|
|
- **iOS:** Swift SDK (SwiftUI & UIKit)
|
|
- **Android:** Kotlin SDK (Jetpack Compose & XML Views)
|
|
- **Flutter:** Cross-Platform SDK
|
|
- **React Native:** Cross-Platform SDK
|
|
|
|
---
|
|
|
|
## 2. Rechtliche Anforderungen
|
|
|
|
### 2.1 DSGVO (EU 2016/679)
|
|
|
|
| Artikel | Anforderung | Umsetzung |
|
|
|---------|-------------|-----------|
|
|
| **Art. 4 Nr. 11** | Einwilligung muss freiwillig, bestimmt, informiert und unmissverständlich sein | Granulare Opt-in-Buttons, keine vorausgewählten Checkboxen |
|
|
| **Art. 6 Abs. 1 lit. a** | Einwilligung als Rechtsgrundlage | Dokumentierte Consent-Erfassung mit Timestamp |
|
|
| **Art. 7 Abs. 1** | Nachweis der Einwilligung | Revisionssichere Speicherung, Audit-Log |
|
|
| **Art. 7 Abs. 2** | Abgrenzung von anderen Sachverhalten | Separate Consent-Abfrage, kein Bundling |
|
|
| **Art. 7 Abs. 3** | Widerruf jederzeit möglich | Persistenter "Einstellungen"-Button, gleichwertige Widerrufsmöglichkeit |
|
|
| **Art. 7 Abs. 4** | Koppelungsverbot | Keine Dienstverweigerung bei Ablehnung |
|
|
| **Art. 12** | Transparente Information | Klare, verständliche Sprache |
|
|
| **Art. 13/14** | Informationspflichten | Verlinkung zur Datenschutzerklärung |
|
|
| **Art. 17** | Recht auf Löschung | Löschung aller Consent-Daten auf Anfrage |
|
|
| **Art. 20** | Datenportabilität | Export aller Consent-Daten als JSON |
|
|
|
|
### 2.2 TTDSG (Deutschland)
|
|
|
|
| § | Anforderung | Umsetzung |
|
|
|---|-------------|-----------|
|
|
| **§ 25 Abs. 1** | Einwilligung für nicht-notwendige Cookies | Opt-in für alle nicht-essentiellen Technologien |
|
|
| **§ 25 Abs. 2** | Ausnahmen für technisch notwendige Speicherung | Klare Kategorisierung "Essentiell" ohne Einwilligung |
|
|
| **§ 25 Abs. 2 Nr. 2** | Ausnahme für vom Nutzer gewünschte Dienste | Dokumentation der Notwendigkeit |
|
|
|
|
### 2.3 ePrivacy-Richtlinie (2002/58/EG)
|
|
|
|
| Artikel | Anforderung | Umsetzung |
|
|
|---------|-------------|-----------|
|
|
| **Art. 5 Abs. 3** | Einwilligung vor Cookie-Setzung | Blockierung aller Skripte bis Consent |
|
|
| **Art. 5 Abs. 3** | Klare Information über Zweck | Transparente Kategoriebeschreibungen |
|
|
|
|
### 2.4 Planet49-Urteil (EuGH C-673/17)
|
|
|
|
**Kernaussagen:**
|
|
- Vorausgewählte Checkboxen sind KEINE gültige Einwilligung
|
|
- Cookie-Laufzeit muss angegeben werden
|
|
- Drittanbieter müssen benannt werden
|
|
|
|
**Umsetzung:**
|
|
- Alle Checkboxen standardmäßig NICHT ausgewählt
|
|
- Anzeige der Cookie-Laufzeit pro Anbieter
|
|
- Auflistung aller Drittanbieter mit Links zu deren Datenschutzerklärung
|
|
|
|
### 2.5 DSK-Orientierungshilfe Telemedien
|
|
|
|
**Anforderungen:**
|
|
1. "Ablehnen" muss genauso prominent sein wie "Akzeptieren"
|
|
2. Keine Dark Patterns (versteckte Ablehnungsoptionen)
|
|
3. Keine Nudging-Techniken (farbliche Hervorhebung von "Akzeptieren")
|
|
4. Direkter Zugang zu granularen Einstellungen
|
|
|
|
### 2.6 BGH-Urteil Cookie-Einwilligung II (2023)
|
|
|
|
**Kernaussagen:**
|
|
- "Alles akzeptieren" nur zulässig wenn gleichwertige "Alles ablehnen"-Option
|
|
- Keine versteckten Ablehungs-Flows
|
|
- Consent-Banner darf Seiteninhalt nicht vollständig verdecken
|
|
|
|
---
|
|
|
|
## 3. Architektur-Übersicht
|
|
|
|
### 3.1 System-Architektur
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ Client-Plattformen │
|
|
├──────────┬──────────┬──────────┬──────────┬──────────┬─────────────────┤
|
|
│ Web │ PWA │ iOS │ Android │ Flutter │ React Native │
|
|
│ SDK │ SDK │ SDK │ SDK │ SDK │ SDK │
|
|
└────┬─────┴────┬─────┴────┬─────┴────┬─────┴────┬─────┴────────┬────────┘
|
|
│ │ │ │ │ │
|
|
└──────────┴──────────┴────┬─────┴──────────┴──────────────┘
|
|
│
|
|
┌───────────▼───────────┐
|
|
│ Consent Gateway │
|
|
│ (REST + WebSocket) │
|
|
└───────────┬───────────┘
|
|
│
|
|
┌──────────────────────────┼──────────────────────────┐
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
│ Consent │ │ Config │ │ Analytics │
|
|
│ Service │ │ Service │ │ Service │
|
|
│ (Go) │ │ (Go) │ │ (Go) │
|
|
└──────┬──────┘ └────────┬────────┘ └────────┬────────┘
|
|
│ │ │
|
|
└──────────────────────┼────────────────────────┘
|
|
│
|
|
┌─────────▼─────────┐
|
|
│ PostgreSQL │
|
|
│ (Consent Store) │
|
|
└───────────────────┘
|
|
```
|
|
|
|
### 3.2 Komponenten
|
|
|
|
| Komponente | Technologie | Beschreibung |
|
|
|------------|-------------|--------------|
|
|
| **Web SDK** | TypeScript | Vanilla JS + Framework-Wrapper |
|
|
| **Mobile SDKs** | Swift/Kotlin | Native Implementierungen |
|
|
| **Consent Gateway** | Go + Gin | API-Gateway mit Rate-Limiting |
|
|
| **Consent Service** | Go | Consent-Logik, bereits vorhanden |
|
|
| **Config Service** | Go | Banner-Konfiguration, TCF-Vendors |
|
|
| **Analytics Service** | Go | Anonymisierte Statistiken |
|
|
| **PostgreSQL** | PostgreSQL 16 | Revisionssichere Speicherung |
|
|
|
|
### 3.3 Datenfluss
|
|
|
|
```
|
|
1. Nutzer öffnet Website/App
|
|
│
|
|
▼
|
|
2. SDK prüft lokalen Consent-Status
|
|
│
|
|
┌────┴────┐
|
|
│ Consent │
|
|
│ exists? │
|
|
└────┬────┘
|
|
No │ Yes
|
|
│ │ │
|
|
▼ │ ▼
|
|
3. Banner│ 5. Apply
|
|
anzeigen Consent
|
|
│
|
|
▼
|
|
4. Nutzer wählt
|
|
Kategorien
|
|
│
|
|
▼
|
|
6. SDK speichert lokal
|
|
+ sendet an Backend
|
|
│
|
|
▼
|
|
7. Backend speichert
|
|
revisionssicher
|
|
│
|
|
▼
|
|
8. SDK blockiert/erlaubt
|
|
entsprechende Skripte
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Consent-Kategorien
|
|
|
|
### 4.1 Standard-Kategorien (IAB TCF 2.2 kompatibel)
|
|
|
|
| ID | Kategorie | Beschreibung | Rechtsgrundlage |
|
|
|----|-----------|--------------|-----------------|
|
|
| `essential` | **Essentiell** | Technisch notwendig für Grundfunktionen | TTDSG § 25 Abs. 2 (keine Einwilligung nötig) |
|
|
| `functional` | **Funktional** | Personalisierung, Spracheinstellungen | DSGVO Art. 6 Abs. 1 lit. a |
|
|
| `analytics` | **Statistik** | Anonyme Nutzungsanalyse | DSGVO Art. 6 Abs. 1 lit. a |
|
|
| `marketing` | **Marketing** | Werbung, Retargeting | DSGVO Art. 6 Abs. 1 lit. a |
|
|
| `social` | **Soziale Medien** | Social Plugins, Sharing | DSGVO Art. 6 Abs. 1 lit. a |
|
|
|
|
### 4.2 Kategorie-Details
|
|
|
|
#### 4.2.1 Essentiell (`essential`)
|
|
|
|
**Beschreibung:** Cookies und Technologien, die für den Betrieb der Website unbedingt erforderlich sind.
|
|
|
|
**Beispiele:**
|
|
- Session-Cookies
|
|
- Warenkorb-Cookies
|
|
- Authentifizierung
|
|
- Load-Balancing
|
|
- CSRF-Token
|
|
- Consent-Cookie selbst
|
|
|
|
**Rechtsgrundlage:** TTDSG § 25 Abs. 2 Nr. 2 - keine Einwilligung erforderlich
|
|
|
|
**UI:** Immer aktiv, nicht deaktivierbar, mit Erklärung warum
|
|
|
|
#### 4.2.2 Funktional (`functional`)
|
|
|
|
**Beschreibung:** Cookies, die die Website-Funktionalität verbessern, aber nicht essentiell sind.
|
|
|
|
**Beispiele:**
|
|
- Sprachpräferenzen
|
|
- Theme-Einstellungen (Dark Mode)
|
|
- Zuletzt angesehene Produkte
|
|
- Chat-Widget-Status
|
|
- Video-Player-Einstellungen
|
|
|
|
**Rechtsgrundlage:** DSGVO Art. 6 Abs. 1 lit. a - Einwilligung erforderlich
|
|
|
|
#### 4.2.3 Statistik (`analytics`)
|
|
|
|
**Beschreibung:** Cookies zur anonymen Analyse des Nutzerverhaltens.
|
|
|
|
**Beispiele:**
|
|
- Google Analytics
|
|
- Matomo/Piwik
|
|
- Hotjar (Heatmaps)
|
|
- Plausible Analytics
|
|
|
|
**Rechtsgrundlage:** DSGVO Art. 6 Abs. 1 lit. a - Einwilligung erforderlich
|
|
|
|
**Hinweis:** Auch bei Anonymisierung ist Einwilligung erforderlich (EuGH-Rechtsprechung)
|
|
|
|
#### 4.2.4 Marketing (`marketing`)
|
|
|
|
**Beschreibung:** Cookies für personalisierte Werbung und Retargeting.
|
|
|
|
**Beispiele:**
|
|
- Google Ads
|
|
- Facebook Pixel
|
|
- LinkedIn Insight Tag
|
|
- Criteo
|
|
- Affiliate-Tracking
|
|
|
|
**Rechtsgrundlage:** DSGVO Art. 6 Abs. 1 lit. a - Einwilligung erforderlich
|
|
|
|
#### 4.2.5 Soziale Medien (`social`)
|
|
|
|
**Beschreibung:** Cookies von Social-Media-Plattformen.
|
|
|
|
**Beispiele:**
|
|
- Facebook Like-Button
|
|
- Twitter Share
|
|
- Instagram Embed
|
|
- YouTube Embed
|
|
- LinkedIn Share
|
|
|
|
**Rechtsgrundlage:** DSGVO Art. 6 Abs. 1 lit. a - Einwilligung erforderlich
|
|
|
|
### 4.3 Anbieter-Granularität (TCF 2.2)
|
|
|
|
Zusätzlich zur Kategorie kann der Nutzer einzelne Anbieter aktivieren/deaktivieren:
|
|
|
|
```typescript
|
|
interface ConsentVendor {
|
|
id: string; // TCF Vendor ID oder custom ID
|
|
name: string; // "Google LLC"
|
|
category: ConsentCategory;
|
|
purposes: TCFPurpose[]; // IAB TCF Purposes
|
|
legitimateInterests?: TCFPurpose[];
|
|
cookies: CookieInfo[];
|
|
privacyPolicyUrl: string;
|
|
dataRetention: string; // "2 Jahre" / "Session"
|
|
dataTransfer?: string; // "USA (EU-US DPF)" / "EU"
|
|
}
|
|
|
|
interface CookieInfo {
|
|
name: string; // "_ga"
|
|
domain: string; // ".example.com"
|
|
expiration: string; // "2 Jahre"
|
|
type: "http" | "localStorage" | "sessionStorage" | "indexedDB";
|
|
description: string;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. SDK-Spezifikation
|
|
|
|
### 5.1 Web SDK (JavaScript/TypeScript)
|
|
|
|
#### 5.1.1 Installation
|
|
|
|
```bash
|
|
# npm
|
|
npm install @breakpilot/consent-sdk
|
|
|
|
# yarn
|
|
yarn add @breakpilot/consent-sdk
|
|
|
|
# pnpm
|
|
pnpm add @breakpilot/consent-sdk
|
|
|
|
# CDN (für Vanilla JS)
|
|
<script src="https://cdn.breakpilot.eu/consent-sdk/v1/consent.min.js"></script>
|
|
```
|
|
|
|
#### 5.1.2 Basis-Konfiguration
|
|
|
|
```typescript
|
|
import { ConsentManager, ConsentConfig } from '@breakpilot/consent-sdk';
|
|
|
|
const config: ConsentConfig = {
|
|
// Pflichtfelder
|
|
apiEndpoint: 'https://consent.example.com/api/v1',
|
|
siteId: 'site_abc123',
|
|
|
|
// Sprache
|
|
language: 'de',
|
|
fallbackLanguage: 'en',
|
|
|
|
// UI-Optionen
|
|
ui: {
|
|
position: 'bottom', // 'bottom' | 'top' | 'center'
|
|
layout: 'bar', // 'bar' | 'modal' | 'floating'
|
|
theme: 'auto', // 'light' | 'dark' | 'auto'
|
|
customCss: '/css/consent.css',
|
|
zIndex: 999999,
|
|
blockScrollOnModal: true,
|
|
},
|
|
|
|
// Consent-Optionen
|
|
consent: {
|
|
required: true, // Muss Nutzer interagieren?
|
|
rejectAllVisible: true, // "Alle ablehnen" Button sichtbar
|
|
acceptAllVisible: true, // "Alle akzeptieren" Button sichtbar
|
|
granularControl: true, // Einzelne Kategorien wählbar
|
|
vendorControl: true, // Einzelne Anbieter wählbar
|
|
rememberChoice: true, // Auswahl speichern
|
|
rememberDays: 365, // Speicherdauer in Tagen
|
|
geoTargeting: true, // Nur in EU anzeigen
|
|
recheckAfterDays: 180, // Erneut nachfragen nach X Tagen
|
|
},
|
|
|
|
// Kategorien aktivieren
|
|
categories: ['essential', 'functional', 'analytics', 'marketing', 'social'],
|
|
|
|
// Callbacks
|
|
onConsentChange: (consent) => {
|
|
console.log('Consent changed:', consent);
|
|
},
|
|
onBannerShow: () => {
|
|
console.log('Banner shown');
|
|
},
|
|
onBannerHide: () => {
|
|
console.log('Banner hidden');
|
|
},
|
|
|
|
// Debug-Modus
|
|
debug: process.env.NODE_ENV === 'development',
|
|
};
|
|
|
|
// Manager initialisieren
|
|
const consent = new ConsentManager(config);
|
|
consent.init();
|
|
```
|
|
|
|
#### 5.1.3 Skript-Blocking
|
|
|
|
```html
|
|
<!-- Skripte mit data-consent blockieren -->
|
|
<script
|
|
data-consent="analytics"
|
|
data-src="https://www.googletagmanager.com/gtag/js?id=GA-XXXXX"
|
|
type="text/plain">
|
|
</script>
|
|
|
|
<!-- Inline-Skripte blockieren -->
|
|
<script data-consent="marketing" type="text/plain">
|
|
fbq('init', 'XXXXX');
|
|
</script>
|
|
|
|
<!-- iFrames blockieren (YouTube, etc.) -->
|
|
<iframe
|
|
data-consent="social"
|
|
data-src="https://www.youtube.com/embed/XXXXX"
|
|
title="YouTube Video">
|
|
<p>Bitte akzeptieren Sie Marketing-Cookies, um dieses Video zu sehen.</p>
|
|
</iframe>
|
|
```
|
|
|
|
#### 5.1.4 API-Methoden
|
|
|
|
```typescript
|
|
// Consent-Status prüfen
|
|
const hasAnalytics = consent.hasConsent('analytics');
|
|
const hasVendor = consent.hasVendorConsent('google-analytics');
|
|
|
|
// Consent programmatisch setzen
|
|
await consent.setConsent({
|
|
essential: true,
|
|
functional: true,
|
|
analytics: false,
|
|
marketing: false,
|
|
social: false,
|
|
});
|
|
|
|
// Alle Einwilligungen widerrufen
|
|
await consent.revokeAll();
|
|
|
|
// Banner erneut anzeigen
|
|
consent.showBanner();
|
|
|
|
// Einstellungs-Modal öffnen
|
|
consent.showSettings();
|
|
|
|
// Consent-Objekt abrufen
|
|
const currentConsent = consent.getConsent();
|
|
// {
|
|
// categories: { essential: true, analytics: false, ... },
|
|
// vendors: { 'google-analytics': false, ... },
|
|
// timestamp: '2026-02-04T12:00:00Z',
|
|
// version: '1.0.0',
|
|
// consentId: 'cns_abc123',
|
|
// }
|
|
|
|
// Consent exportieren (für Nutzeranfragen)
|
|
const exportData = consent.exportConsent();
|
|
// JSON mit allen historischen Consents
|
|
|
|
// Event-Listener
|
|
consent.on('change', (newConsent) => { ... });
|
|
consent.on('vendor:enable', (vendorId) => { ... });
|
|
consent.on('vendor:disable', (vendorId) => { ... });
|
|
```
|
|
|
|
#### 5.1.5 Framework-Integrationen
|
|
|
|
**React:**
|
|
|
|
```tsx
|
|
import { ConsentProvider, useConsent, ConsentBanner } from '@breakpilot/consent-sdk/react';
|
|
|
|
function App() {
|
|
return (
|
|
<ConsentProvider config={config}>
|
|
<ConsentBanner />
|
|
<MainContent />
|
|
</ConsentProvider>
|
|
);
|
|
}
|
|
|
|
function AnalyticsComponent() {
|
|
const { hasConsent, isLoading } = useConsent('analytics');
|
|
|
|
if (isLoading) return <Skeleton />;
|
|
if (!hasConsent) return <ConsentPlaceholder category="analytics" />;
|
|
|
|
return <GoogleAnalytics />;
|
|
}
|
|
```
|
|
|
|
**Vue 3:**
|
|
|
|
```vue
|
|
<template>
|
|
<ConsentBanner />
|
|
<AnalyticsComponent v-if="hasConsent('analytics')" />
|
|
<ConsentPlaceholder v-else category="analytics" />
|
|
</template>
|
|
|
|
<script setup>
|
|
import { useConsent, ConsentBanner, ConsentPlaceholder } from '@breakpilot/consent-sdk/vue';
|
|
|
|
const { hasConsent } = useConsent();
|
|
</script>
|
|
```
|
|
|
|
**Angular:**
|
|
|
|
```typescript
|
|
import { ConsentModule } from '@breakpilot/consent-sdk/angular';
|
|
|
|
@NgModule({
|
|
imports: [
|
|
ConsentModule.forRoot(config),
|
|
],
|
|
})
|
|
export class AppModule {}
|
|
|
|
// In Component:
|
|
@Component({...})
|
|
export class MyComponent {
|
|
constructor(private consent: ConsentService) {}
|
|
|
|
get hasAnalytics(): boolean {
|
|
return this.consent.hasConsent('analytics');
|
|
}
|
|
}
|
|
```
|
|
|
|
### 5.2 PWA-spezifische Features
|
|
|
|
#### 5.2.1 Offline-Support
|
|
|
|
```typescript
|
|
const pwaConfig: ConsentConfig = {
|
|
...baseConfig,
|
|
pwa: {
|
|
offlineSupport: true,
|
|
syncOnReconnect: true,
|
|
cacheStrategy: 'stale-while-revalidate',
|
|
},
|
|
};
|
|
|
|
// Service Worker Integration
|
|
// consent-sw.js wird automatisch registriert
|
|
```
|
|
|
|
#### 5.2.2 App-Banner Integration
|
|
|
|
```typescript
|
|
// Consent vor PWA-Install-Banner abfragen
|
|
consent.on('beforeInstallPrompt', (event) => {
|
|
if (!consent.hasConsent('functional')) {
|
|
event.preventDefault();
|
|
consent.showBanner({
|
|
message: 'Für die App-Installation benötigen wir Ihre Einwilligung.',
|
|
highlightCategory: 'functional',
|
|
});
|
|
}
|
|
});
|
|
```
|
|
|
|
### 5.3 iOS SDK (Swift)
|
|
|
|
#### 5.3.1 Installation
|
|
|
|
```swift
|
|
// Swift Package Manager
|
|
dependencies: [
|
|
.package(url: "https://github.com/breakpilot/consent-sdk-ios", from: "1.0.0")
|
|
]
|
|
|
|
// CocoaPods
|
|
pod 'BreakPilotConsent', '~> 1.0'
|
|
```
|
|
|
|
#### 5.3.2 Konfiguration
|
|
|
|
```swift
|
|
import BreakPilotConsent
|
|
|
|
let config = ConsentConfig(
|
|
apiEndpoint: "https://consent.example.com/api/v1",
|
|
siteId: "site_abc123",
|
|
language: .german,
|
|
categories: [.essential, .functional, .analytics, .marketing]
|
|
)
|
|
|
|
// AppDelegate oder SceneDelegate
|
|
ConsentManager.shared.configure(with: config)
|
|
```
|
|
|
|
#### 5.3.3 SwiftUI Integration
|
|
|
|
```swift
|
|
import SwiftUI
|
|
import BreakPilotConsent
|
|
|
|
struct ContentView: View {
|
|
@StateObject private var consent = ConsentManager.shared
|
|
|
|
var body: some View {
|
|
VStack {
|
|
if consent.needsConsent {
|
|
ConsentBannerView()
|
|
}
|
|
|
|
MainContent()
|
|
}
|
|
.consentGate(category: .analytics) {
|
|
AnalyticsView()
|
|
} placeholder: {
|
|
ConsentPlaceholder(category: .analytics)
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 5.3.4 UIKit Integration
|
|
|
|
```swift
|
|
import BreakPilotConsent
|
|
|
|
class ViewController: UIViewController {
|
|
override func viewDidAppear(_ animated: Bool) {
|
|
super.viewDidAppear(animated)
|
|
|
|
if ConsentManager.shared.needsConsent {
|
|
let bannerVC = ConsentBannerViewController()
|
|
bannerVC.modalPresentationStyle = .overFullScreen
|
|
present(bannerVC, animated: true)
|
|
}
|
|
}
|
|
|
|
func loadAnalytics() {
|
|
guard ConsentManager.shared.hasConsent(.analytics) else {
|
|
showConsentRequired(for: .analytics)
|
|
return
|
|
}
|
|
// Analytics laden
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 5.3.5 App Tracking Transparency (ATT) Integration
|
|
|
|
```swift
|
|
import AppTrackingTransparency
|
|
|
|
// Consent-SDK koordiniert mit ATT
|
|
ConsentManager.shared.configure(with: config) { manager in
|
|
// Nach Consent-Banner ATT-Dialog anzeigen
|
|
if manager.hasConsent(.marketing) {
|
|
ATTrackingManager.requestTrackingAuthorization { status in
|
|
switch status {
|
|
case .authorized:
|
|
manager.updateATTStatus(.authorized)
|
|
case .denied, .restricted:
|
|
manager.revokeConsent(.marketing)
|
|
case .notDetermined:
|
|
break
|
|
@unknown default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 5.4 Android SDK (Kotlin)
|
|
|
|
#### 5.4.1 Installation
|
|
|
|
```kotlin
|
|
// build.gradle.kts
|
|
dependencies {
|
|
implementation("eu.breakpilot:consent-sdk:1.0.0")
|
|
}
|
|
```
|
|
|
|
#### 5.4.2 Konfiguration
|
|
|
|
```kotlin
|
|
import eu.breakpilot.consent.ConsentManager
|
|
import eu.breakpilot.consent.ConsentConfig
|
|
|
|
class MyApplication : Application() {
|
|
override fun onCreate() {
|
|
super.onCreate()
|
|
|
|
val config = ConsentConfig.Builder()
|
|
.apiEndpoint("https://consent.example.com/api/v1")
|
|
.siteId("site_abc123")
|
|
.language(Language.GERMAN)
|
|
.categories(
|
|
Category.ESSENTIAL,
|
|
Category.FUNCTIONAL,
|
|
Category.ANALYTICS,
|
|
Category.MARKETING
|
|
)
|
|
.build()
|
|
|
|
ConsentManager.initialize(this, config)
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 5.4.3 Jetpack Compose Integration
|
|
|
|
```kotlin
|
|
import eu.breakpilot.consent.compose.*
|
|
|
|
@Composable
|
|
fun MainScreen() {
|
|
val consent = rememberConsentState()
|
|
|
|
if (consent.needsConsent) {
|
|
ConsentBanner(
|
|
onAcceptAll = { consent.acceptAll() },
|
|
onRejectAll = { consent.rejectAll() },
|
|
onCustomize = { /* Öffne Einstellungen */ }
|
|
)
|
|
}
|
|
|
|
ConsentGate(
|
|
category = Category.ANALYTICS,
|
|
placeholder = { ConsentPlaceholder(Category.ANALYTICS) }
|
|
) {
|
|
AnalyticsContent()
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 5.4.4 XML Views Integration
|
|
|
|
```kotlin
|
|
class MainActivity : AppCompatActivity() {
|
|
private val consent = ConsentManager.instance
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
|
|
consent.observe(this) { state ->
|
|
when {
|
|
state.needsConsent -> showConsentBanner()
|
|
state.hasConsent(Category.ANALYTICS) -> loadAnalytics()
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun showConsentBanner() {
|
|
ConsentBannerFragment.show(supportFragmentManager)
|
|
}
|
|
}
|
|
```
|
|
|
|
### 5.5 Flutter SDK
|
|
|
|
```dart
|
|
import 'package:breakpilot_consent/breakpilot_consent.dart';
|
|
|
|
void main() async {
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
|
|
await ConsentManager.initialize(
|
|
config: ConsentConfig(
|
|
apiEndpoint: 'https://consent.example.com/api/v1',
|
|
siteId: 'site_abc123',
|
|
language: Language.german,
|
|
categories: [
|
|
Category.essential,
|
|
Category.functional,
|
|
Category.analytics,
|
|
Category.marketing,
|
|
],
|
|
),
|
|
);
|
|
|
|
runApp(MyApp());
|
|
}
|
|
|
|
class MyApp extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ConsentProvider(
|
|
child: MaterialApp(
|
|
home: ConsentAwareHome(),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class ConsentAwareHome extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ConsentBuilder(
|
|
builder: (context, consent) {
|
|
if (consent.needsConsent) {
|
|
return ConsentBanner();
|
|
}
|
|
|
|
return Scaffold(
|
|
body: Column(
|
|
children: [
|
|
ConsentGate(
|
|
category: Category.analytics,
|
|
child: AnalyticsWidget(),
|
|
placeholder: ConsentPlaceholder(category: Category.analytics),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### 5.6 React Native SDK
|
|
|
|
```tsx
|
|
import {
|
|
ConsentProvider,
|
|
useConsent,
|
|
ConsentBanner,
|
|
ConsentGate,
|
|
} from '@breakpilot/consent-sdk-react-native';
|
|
|
|
const config = {
|
|
apiEndpoint: 'https://consent.example.com/api/v1',
|
|
siteId: 'site_abc123',
|
|
language: 'de',
|
|
categories: ['essential', 'functional', 'analytics', 'marketing'],
|
|
};
|
|
|
|
function App() {
|
|
return (
|
|
<ConsentProvider config={config}>
|
|
<NavigationContainer>
|
|
<MainStack />
|
|
</NavigationContainer>
|
|
<ConsentBanner />
|
|
</ConsentProvider>
|
|
);
|
|
}
|
|
|
|
function AnalyticsScreen() {
|
|
const { hasConsent } = useConsent();
|
|
|
|
if (!hasConsent('analytics')) {
|
|
return <ConsentRequired category="analytics" />;
|
|
}
|
|
|
|
return <AnalyticsContent />;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. API-Spezifikation
|
|
|
|
### 6.1 Base URL
|
|
|
|
```
|
|
Production: https://consent.breakpilot.eu/api/v1
|
|
Staging: https://consent-staging.breakpilot.eu/api/v1
|
|
```
|
|
|
|
### 6.2 Authentifizierung
|
|
|
|
```http
|
|
# API-Key für Server-to-Server
|
|
Authorization: Bearer <api_key>
|
|
|
|
# HMAC für Client-SDKs (verhindert Manipulation)
|
|
X-Consent-Signature: sha256=<hmac_signature>
|
|
X-Consent-Timestamp: 1707000000
|
|
```
|
|
|
|
### 6.3 Endpoints
|
|
|
|
#### 6.3.1 Consent erstellen/aktualisieren
|
|
|
|
```http
|
|
POST /consent
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"siteId": "site_abc123",
|
|
"userId": "user_xyz789", // Optional: Für eingeloggte Nutzer
|
|
"deviceFingerprint": "fp_...", // Anonymisierter Fingerprint
|
|
"consent": {
|
|
"categories": {
|
|
"essential": true,
|
|
"functional": true,
|
|
"analytics": false,
|
|
"marketing": false,
|
|
"social": false
|
|
},
|
|
"vendors": {
|
|
"google-analytics": false,
|
|
"facebook-pixel": false
|
|
}
|
|
},
|
|
"metadata": {
|
|
"userAgent": "Mozilla/5.0...",
|
|
"language": "de-DE",
|
|
"screenResolution": "1920x1080",
|
|
"platform": "web",
|
|
"appVersion": "1.0.0"
|
|
}
|
|
}
|
|
|
|
Response 201:
|
|
{
|
|
"consentId": "cns_abc123def456",
|
|
"timestamp": "2026-02-04T12:00:00Z",
|
|
"expiresAt": "2027-02-04T12:00:00Z",
|
|
"version": "1.0.0"
|
|
}
|
|
```
|
|
|
|
#### 6.3.2 Consent abrufen
|
|
|
|
```http
|
|
GET /consent?siteId=site_abc123&deviceFingerprint=fp_...
|
|
|
|
Response 200:
|
|
{
|
|
"consentId": "cns_abc123def456",
|
|
"consent": {
|
|
"categories": {
|
|
"essential": true,
|
|
"functional": true,
|
|
"analytics": false,
|
|
"marketing": false,
|
|
"social": false
|
|
},
|
|
"vendors": {}
|
|
},
|
|
"createdAt": "2026-02-04T12:00:00Z",
|
|
"updatedAt": "2026-02-04T12:00:00Z",
|
|
"expiresAt": "2027-02-04T12:00:00Z",
|
|
"version": "1.0.0"
|
|
}
|
|
|
|
Response 404:
|
|
{
|
|
"error": "consent_not_found",
|
|
"message": "No consent record found"
|
|
}
|
|
```
|
|
|
|
#### 6.3.3 Consent widerrufen
|
|
|
|
```http
|
|
DELETE /consent/{consentId}
|
|
|
|
Response 200:
|
|
{
|
|
"status": "revoked",
|
|
"revokedAt": "2026-02-04T13:00:00Z"
|
|
}
|
|
```
|
|
|
|
#### 6.3.4 Consent-Historie exportieren (DSGVO Art. 20)
|
|
|
|
```http
|
|
GET /consent/export?userId=user_xyz789
|
|
|
|
Response 200:
|
|
{
|
|
"userId": "user_xyz789",
|
|
"exportedAt": "2026-02-04T12:00:00Z",
|
|
"consents": [
|
|
{
|
|
"consentId": "cns_abc123",
|
|
"siteId": "site_abc123",
|
|
"consent": { ... },
|
|
"createdAt": "2026-01-01T12:00:00Z",
|
|
"revokedAt": null
|
|
},
|
|
{
|
|
"consentId": "cns_def456",
|
|
"siteId": "site_abc123",
|
|
"consent": { ... },
|
|
"createdAt": "2025-06-01T12:00:00Z",
|
|
"revokedAt": "2026-01-01T11:59:00Z"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### 6.3.5 Site-Konfiguration abrufen
|
|
|
|
```http
|
|
GET /config/{siteId}
|
|
|
|
Response 200:
|
|
{
|
|
"siteId": "site_abc123",
|
|
"siteName": "Example Website",
|
|
"categories": [
|
|
{
|
|
"id": "essential",
|
|
"name": { "de": "Essentiell", "en": "Essential" },
|
|
"description": { "de": "...", "en": "..." },
|
|
"required": true,
|
|
"vendors": []
|
|
},
|
|
{
|
|
"id": "analytics",
|
|
"name": { "de": "Statistik", "en": "Analytics" },
|
|
"description": { "de": "...", "en": "..." },
|
|
"required": false,
|
|
"vendors": [
|
|
{
|
|
"id": "google-analytics",
|
|
"name": "Google Analytics",
|
|
"privacyPolicyUrl": "https://policies.google.com/privacy",
|
|
"cookies": [
|
|
{
|
|
"name": "_ga",
|
|
"expiration": "2 Jahre",
|
|
"description": "Unterscheidet Nutzer"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"ui": {
|
|
"theme": "light",
|
|
"position": "bottom",
|
|
"customCss": "..."
|
|
},
|
|
"legal": {
|
|
"privacyPolicyUrl": "https://example.com/datenschutz",
|
|
"imprintUrl": "https://example.com/impressum",
|
|
"dpo": {
|
|
"name": "Max Mustermann",
|
|
"email": "datenschutz@example.com"
|
|
}
|
|
},
|
|
"tcf": {
|
|
"enabled": true,
|
|
"cmpId": 123,
|
|
"cmpVersion": 1
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 6.3.6 Consent-Statistiken (Admin)
|
|
|
|
```http
|
|
GET /admin/stats/{siteId}
|
|
Authorization: Bearer <admin_api_key>
|
|
|
|
Response 200:
|
|
{
|
|
"siteId": "site_abc123",
|
|
"period": {
|
|
"from": "2026-01-01",
|
|
"to": "2026-02-04"
|
|
},
|
|
"totalBannerViews": 150000,
|
|
"totalInteractions": 120000,
|
|
"interactionRate": 0.80,
|
|
"consentByCategory": {
|
|
"essential": { "accepted": 120000, "rate": 1.0 },
|
|
"functional": { "accepted": 95000, "rate": 0.79 },
|
|
"analytics": { "accepted": 45000, "rate": 0.38 },
|
|
"marketing": { "accepted": 25000, "rate": 0.21 },
|
|
"social": { "accepted": 30000, "rate": 0.25 }
|
|
},
|
|
"actions": {
|
|
"acceptAll": 35000,
|
|
"rejectAll": 40000,
|
|
"customize": 45000
|
|
},
|
|
"avgTimeToDecision": "4.2s",
|
|
"bounceAfterBanner": 0.05
|
|
}
|
|
```
|
|
|
|
### 6.4 Webhooks
|
|
|
|
```http
|
|
POST https://your-server.com/webhooks/consent
|
|
|
|
{
|
|
"event": "consent.updated",
|
|
"timestamp": "2026-02-04T12:00:00Z",
|
|
"data": {
|
|
"consentId": "cns_abc123",
|
|
"siteId": "site_abc123",
|
|
"userId": "user_xyz789",
|
|
"changes": {
|
|
"analytics": { "from": true, "to": false },
|
|
"marketing": { "from": false, "to": false }
|
|
}
|
|
}
|
|
}
|
|
|
|
// Event-Typen:
|
|
// - consent.created
|
|
// - consent.updated
|
|
// - consent.revoked
|
|
// - consent.expired
|
|
```
|
|
|
|
---
|
|
|
|
## 7. UI/UX-Anforderungen
|
|
|
|
### 7.1 Design-Prinzipien
|
|
|
|
1. **Gleichwertigkeit:** "Ablehnen" und "Akzeptieren" gleich prominent
|
|
2. **Transparenz:** Alle Informationen auf einen Blick
|
|
3. **Barrierefreiheit:** WCAG 2.1 AA konform
|
|
4. **Mobile-First:** Responsive Design
|
|
5. **Keine Dark Patterns:** Keine manipulativen Design-Elemente
|
|
|
|
### 7.2 Banner-Layouts
|
|
|
|
#### 7.2.1 Bottom Bar (Standard)
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ 🍪 Diese Website verwendet Cookies │
|
|
│ │
|
|
│ Wir nutzen Cookies und ähnliche Technologien, um Ihnen ein │
|
|
│ optimales Nutzererlebnis zu bieten. [Mehr erfahren] │
|
|
│ │
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
|
|
│ │ Alle ablehnen│ │ Einstellungen │ │ Alle akzeptieren │ │
|
|
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
#### 7.2.2 Modal (DSGVO-empfohlen)
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ [X] │
|
|
│ 🍪 Datenschutzeinstellungen │
|
|
│ │
|
|
│ ───────────────────────────────────────────────────────────────────── │
|
|
│ │
|
|
│ Wir respektieren Ihre Privatsphäre. Bitte wählen Sie, welche │
|
|
│ Cookies Sie akzeptieren möchten. │
|
|
│ │
|
|
│ ☑ Essentiell (immer aktiv) ⓘ │
|
|
│ Notwendig für die Grundfunktionen der Website. │
|
|
│ │
|
|
│ ☐ Funktional ⓘ │
|
|
│ Personalisierung und Komfortfunktionen. │
|
|
│ │
|
|
│ ☐ Statistik ⓘ │
|
|
│ Hilft uns, die Website zu verbessern. │
|
|
│ │
|
|
│ ☐ Marketing ⓘ │
|
|
│ Personalisierte Werbung. │
|
|
│ │
|
|
│ ☐ Soziale Medien ⓘ │
|
|
│ Inhalte von sozialen Netzwerken. │
|
|
│ │
|
|
│ ───────────────────────────────────────────────────────────────────── │
|
|
│ │
|
|
│ [Datenschutzerklärung] [Impressum] [Cookie-Details] │
|
|
│ │
|
|
│ ┌──────────────────┐ ┌──────────────────────────┐ │
|
|
│ │ Auswahl speichern │ │ Alle akzeptieren │ │
|
|
│ └──────────────────┘ └──────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ Alle ablehnen │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 7.3 Farbschema
|
|
|
|
```scss
|
|
// Light Theme
|
|
$consent-bg: #ffffff;
|
|
$consent-text: #1a1a1a;
|
|
$consent-secondary: #666666;
|
|
$consent-border: #e0e0e0;
|
|
|
|
$button-accept-bg: #2563eb; // Blau (neutral, nicht grün)
|
|
$button-accept-text: #ffffff;
|
|
|
|
$button-reject-bg: #ffffff; // Gleiche Prominenz!
|
|
$button-reject-text: #1a1a1a;
|
|
$button-reject-border: #2563eb;
|
|
|
|
$button-settings-bg: #f3f4f6;
|
|
$button-settings-text: #1a1a1a;
|
|
|
|
// Dark Theme
|
|
$consent-bg-dark: #1f2937;
|
|
$consent-text-dark: #f9fafb;
|
|
// ...
|
|
```
|
|
|
|
### 7.4 Barrierefreiheit (WCAG 2.1 AA)
|
|
|
|
| Anforderung | Umsetzung |
|
|
|-------------|-----------|
|
|
| **Kontrast** | Mindestens 4.5:1 für Text |
|
|
| **Fokus** | Sichtbare Fokus-Indikatoren |
|
|
| **Tastatur** | Vollständig per Tastatur bedienbar |
|
|
| **Screen Reader** | ARIA-Labels für alle Elemente |
|
|
| **Zoom** | Funktioniert bei 200% Zoom |
|
|
| **Motion** | Respektiert `prefers-reduced-motion` |
|
|
|
|
```html
|
|
<!-- Beispiel: Barrierefreier Button -->
|
|
<button
|
|
role="button"
|
|
aria-label="Alle Cookies akzeptieren"
|
|
aria-describedby="consent-description"
|
|
tabindex="0"
|
|
class="consent-accept-btn"
|
|
>
|
|
Alle akzeptieren
|
|
</button>
|
|
```
|
|
|
|
### 7.5 Animationen
|
|
|
|
```css
|
|
/* Respektiert Nutzereinstellung */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.consent-banner {
|
|
animation: none;
|
|
transition: none;
|
|
}
|
|
}
|
|
|
|
/* Dezente Einblendung */
|
|
@media (prefers-reduced-motion: no-preference) {
|
|
.consent-banner {
|
|
animation: slideUp 0.3s ease-out;
|
|
}
|
|
|
|
@keyframes slideUp {
|
|
from {
|
|
transform: translateY(100%);
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
transform: translateY(0);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 7.6 Mehrsprachigkeit
|
|
|
|
```typescript
|
|
const translations = {
|
|
de: {
|
|
title: 'Datenschutzeinstellungen',
|
|
description: 'Wir nutzen Cookies und ähnliche Technologien...',
|
|
acceptAll: 'Alle akzeptieren',
|
|
rejectAll: 'Alle ablehnen',
|
|
settings: 'Einstellungen',
|
|
saveSelection: 'Auswahl speichern',
|
|
categories: {
|
|
essential: {
|
|
name: 'Essentiell',
|
|
description: 'Notwendig für die Grundfunktionen.',
|
|
},
|
|
analytics: {
|
|
name: 'Statistik',
|
|
description: 'Hilft uns, die Website zu verbessern.',
|
|
},
|
|
// ...
|
|
},
|
|
footer: {
|
|
privacyPolicy: 'Datenschutzerklärung',
|
|
imprint: 'Impressum',
|
|
cookieDetails: 'Cookie-Details',
|
|
},
|
|
},
|
|
en: {
|
|
title: 'Privacy Settings',
|
|
// ...
|
|
},
|
|
fr: { ... },
|
|
es: { ... },
|
|
it: { ... },
|
|
nl: { ... },
|
|
pl: { ... },
|
|
// Alle EU-Sprachen
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Plattform-Integrationen
|
|
|
|
### 8.1 Google Tag Manager
|
|
|
|
```javascript
|
|
// GTM-Template für Consent Mode v2
|
|
consent.on('change', (consentState) => {
|
|
gtag('consent', 'update', {
|
|
'ad_storage': consentState.marketing ? 'granted' : 'denied',
|
|
'ad_user_data': consentState.marketing ? 'granted' : 'denied',
|
|
'ad_personalization': consentState.marketing ? 'granted' : 'denied',
|
|
'analytics_storage': consentState.analytics ? 'granted' : 'denied',
|
|
'functionality_storage': consentState.functional ? 'granted' : 'denied',
|
|
'personalization_storage': consentState.functional ? 'granted' : 'denied',
|
|
'security_storage': 'granted', // Immer erlaubt
|
|
});
|
|
});
|
|
```
|
|
|
|
### 8.2 Meta (Facebook) Pixel
|
|
|
|
```javascript
|
|
consent.on('change', (consentState) => {
|
|
if (typeof fbq !== 'undefined') {
|
|
if (consentState.marketing) {
|
|
fbq('consent', 'grant');
|
|
} else {
|
|
fbq('consent', 'revoke');
|
|
}
|
|
}
|
|
});
|
|
```
|
|
|
|
### 8.3 Google Analytics 4
|
|
|
|
```javascript
|
|
// GA4 mit Consent Mode
|
|
consent.on('change', (consentState) => {
|
|
gtag('consent', 'update', {
|
|
'analytics_storage': consentState.analytics ? 'granted' : 'denied',
|
|
});
|
|
});
|
|
```
|
|
|
|
### 8.4 IAB TCF 2.2
|
|
|
|
```javascript
|
|
// TCF API Integration
|
|
window.__tcfapi = window.__tcfapi || function() {
|
|
(window.__tcfapi.a = window.__tcfapi.a || []).push(arguments);
|
|
};
|
|
|
|
consent.on('change', (consentState, tcfString) => {
|
|
// TC String an CMP-API übergeben
|
|
__tcfapi('setConsentInfo', 2, (success) => {
|
|
if (success) {
|
|
console.log('TCF Consent updated');
|
|
}
|
|
}, { tcString: tcfString });
|
|
});
|
|
```
|
|
|
|
### 8.5 WordPress Plugin
|
|
|
|
```php
|
|
<?php
|
|
/**
|
|
* Plugin Name: BreakPilot Consent
|
|
* Description: DSGVO-konformes Cookie-Banner
|
|
* Version: 1.0.0
|
|
* License: Apache-2.0
|
|
*/
|
|
|
|
function breakpilot_consent_enqueue() {
|
|
wp_enqueue_script(
|
|
'breakpilot-consent',
|
|
'https://cdn.breakpilot.eu/consent-sdk/v1/consent.min.js',
|
|
[],
|
|
'1.0.0',
|
|
true
|
|
);
|
|
|
|
wp_add_inline_script('breakpilot-consent', '
|
|
new ConsentManager({
|
|
apiEndpoint: "' . esc_js(get_option('breakpilot_api_endpoint')) . '",
|
|
siteId: "' . esc_js(get_option('breakpilot_site_id')) . '",
|
|
language: "' . esc_js(get_locale()) . '"
|
|
}).init();
|
|
');
|
|
}
|
|
add_action('wp_enqueue_scripts', 'breakpilot_consent_enqueue');
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Datenspeicherung & Audit
|
|
|
|
### 9.1 Datenbank-Schema
|
|
|
|
```sql
|
|
-- Consent-Einträge
|
|
CREATE TABLE consent_records (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
site_id VARCHAR(50) NOT NULL,
|
|
user_id VARCHAR(100), -- Optional
|
|
device_fingerprint VARCHAR(64) NOT NULL, -- SHA-256 Hash
|
|
|
|
-- Consent-Daten
|
|
categories JSONB NOT NULL, -- { "analytics": true, ... }
|
|
vendors JSONB DEFAULT '{}', -- { "google-analytics": true }
|
|
tcf_string TEXT, -- IAB TCF String
|
|
|
|
-- Metadaten
|
|
ip_hash VARCHAR(64), -- Anonymisiert
|
|
user_agent TEXT,
|
|
language VARCHAR(10),
|
|
platform VARCHAR(20), -- web, ios, android
|
|
app_version VARCHAR(20),
|
|
|
|
-- Zeitstempel
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
expires_at TIMESTAMPTZ,
|
|
revoked_at TIMESTAMPTZ,
|
|
|
|
-- Versionierung
|
|
version VARCHAR(20) DEFAULT '1.0.0',
|
|
|
|
-- Indizes
|
|
CONSTRAINT fk_site FOREIGN KEY (site_id) REFERENCES sites(id)
|
|
);
|
|
|
|
CREATE INDEX idx_consent_site ON consent_records(site_id);
|
|
CREATE INDEX idx_consent_user ON consent_records(user_id) WHERE user_id IS NOT NULL;
|
|
CREATE INDEX idx_consent_device ON consent_records(device_fingerprint);
|
|
CREATE INDEX idx_consent_created ON consent_records(created_at);
|
|
|
|
-- Audit-Log (unveränderbar)
|
|
CREATE TABLE consent_audit_log (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
consent_id UUID REFERENCES consent_records(id),
|
|
action VARCHAR(20) NOT NULL, -- created, updated, revoked
|
|
previous_state JSONB,
|
|
new_state JSONB,
|
|
ip_hash VARCHAR(64),
|
|
user_agent TEXT,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Kein UPDATE/DELETE auf Audit-Log
|
|
REVOKE UPDATE, DELETE ON consent_audit_log FROM PUBLIC;
|
|
```
|
|
|
|
### 9.2 Datenaufbewahrung
|
|
|
|
| Datentyp | Aufbewahrung | Begründung |
|
|
|----------|--------------|------------|
|
|
| **Consent-Record** | 3 Jahre | Nachweis der Einwilligung (Art. 7 Abs. 1 DSGVO) |
|
|
| **Audit-Log** | 6 Jahre | Steuerrechtliche Aufbewahrungspflicht |
|
|
| **Statistiken** | Unbegrenzt | Anonymisiert, kein Personenbezug |
|
|
| **Abgelaufene Consents** | 30 Tage | Grace Period für Sync-Konflikte |
|
|
|
|
### 9.3 Löschkonzept
|
|
|
|
```go
|
|
// Automatische Bereinigung
|
|
func (s *ConsentService) CleanupExpiredRecords(ctx context.Context) error {
|
|
// Soft-Delete für normale Records
|
|
_, err := s.db.ExecContext(ctx, `
|
|
UPDATE consent_records
|
|
SET deleted_at = NOW()
|
|
WHERE expires_at < NOW() - INTERVAL '30 days'
|
|
AND deleted_at IS NULL
|
|
`)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Hard-Delete nach 3 Jahren
|
|
_, err = s.db.ExecContext(ctx, `
|
|
DELETE FROM consent_records
|
|
WHERE deleted_at < NOW() - INTERVAL '3 years'
|
|
`)
|
|
return err
|
|
}
|
|
```
|
|
|
|
### 9.4 Audit-Trail
|
|
|
|
Jede Consent-Änderung wird protokolliert:
|
|
|
|
```go
|
|
type AuditEntry struct {
|
|
ConsentID uuid.UUID `json:"consent_id"`
|
|
Action string `json:"action"` // created, updated, revoked
|
|
PreviousState *Consent `json:"previous_state"`
|
|
NewState *Consent `json:"new_state"`
|
|
IPHash string `json:"ip_hash"`
|
|
UserAgent string `json:"user_agent"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
}
|
|
|
|
func (s *ConsentService) logAudit(ctx context.Context, entry AuditEntry) error {
|
|
_, err := s.db.ExecContext(ctx, `
|
|
INSERT INTO consent_audit_log
|
|
(consent_id, action, previous_state, new_state, ip_hash, user_agent)
|
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
`, entry.ConsentID, entry.Action,
|
|
entry.PreviousState, entry.NewState,
|
|
entry.IPHash, entry.UserAgent)
|
|
return err
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Sicherheit & Privacy
|
|
|
|
### 10.1 Datenschutz by Design
|
|
|
|
| Prinzip | Umsetzung |
|
|
|---------|-----------|
|
|
| **Datenminimierung** | Nur notwendige Daten speichern |
|
|
| **Pseudonymisierung** | Device-Fingerprint statt direkter Identifikation |
|
|
| **Zweckbindung** | Daten nur für Consent-Nachweis verwenden |
|
|
| **Transparenz** | Alle Datenverarbeitung dokumentiert |
|
|
|
|
### 10.2 Device Fingerprinting
|
|
|
|
```typescript
|
|
// Datenschutzkonformes Fingerprinting
|
|
function generateFingerprint(): string {
|
|
const components = [
|
|
navigator.userAgent,
|
|
navigator.language,
|
|
screen.colorDepth.toString(),
|
|
new Date().getTimezoneOffset().toString(),
|
|
// KEINE Canvas-Fingerprints oder Hardware-IDs
|
|
];
|
|
|
|
// SHA-256 Hash für Anonymisierung
|
|
return sha256(components.join('|'));
|
|
}
|
|
```
|
|
|
|
### 10.3 IP-Anonymisierung
|
|
|
|
```go
|
|
func anonymizeIP(ip string) string {
|
|
parsed := net.ParseIP(ip)
|
|
if parsed == nil {
|
|
return ""
|
|
}
|
|
|
|
if parsed.To4() != nil {
|
|
// IPv4: Letzte 8 Bit auf 0
|
|
// 192.168.1.123 -> 192.168.1.0
|
|
parsed[15] = 0
|
|
} else {
|
|
// IPv6: Letzte 80 Bit auf 0
|
|
for i := 6; i < 16; i++ {
|
|
parsed[i] = 0
|
|
}
|
|
}
|
|
|
|
// Hash für zusätzliche Sicherheit
|
|
return sha256(parsed.String())
|
|
}
|
|
```
|
|
|
|
### 10.4 Verschlüsselung
|
|
|
|
| Daten | Verschlüsselung |
|
|
|-------|-----------------|
|
|
| **In Transit** | TLS 1.3 |
|
|
| **At Rest** | AES-256-GCM |
|
|
| **Consent-Cookie** | HMAC-SHA256 signiert |
|
|
| **API-Keys** | bcrypt (Faktor 12) |
|
|
|
|
### 10.5 Rate Limiting
|
|
|
|
```go
|
|
// Pro IP-Adresse
|
|
var limiter = rate.NewLimiter(
|
|
rate.Every(time.Second), // 1 Request/Sekunde
|
|
10, // Burst von 10
|
|
)
|
|
|
|
// Pro Site
|
|
var siteLimiter = rate.NewLimiter(
|
|
rate.Every(time.Millisecond * 100), // 10 Requests/Sekunde
|
|
100, // Burst von 100
|
|
)
|
|
```
|
|
|
|
### 10.6 CORS-Konfiguration
|
|
|
|
```go
|
|
func corsMiddleware() gin.HandlerFunc {
|
|
return cors.New(cors.Config{
|
|
AllowOrigins: []string{"https://*.example.com"},
|
|
AllowMethods: []string{"GET", "POST", "DELETE"},
|
|
AllowHeaders: []string{"Content-Type", "X-Consent-Signature"},
|
|
AllowCredentials: true,
|
|
MaxAge: 12 * time.Hour,
|
|
})
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 11. Implementierungs-Roadmap
|
|
|
|
### Phase 1: Core SDK (8 Wochen)
|
|
|
|
| Woche | Aufgabe |
|
|
|-------|---------|
|
|
| 1-2 | Web SDK Basis (TypeScript) |
|
|
| 3-4 | Backend API (Go) |
|
|
| 5-6 | React/Vue Integration |
|
|
| 7-8 | Testing & Dokumentation |
|
|
|
|
**Deliverables:**
|
|
- Web SDK v1.0.0
|
|
- REST API v1.0.0
|
|
- React/Vue Wrapper
|
|
- Dokumentation
|
|
|
|
### Phase 2: Mobile SDKs (6 Wochen)
|
|
|
|
| Woche | Aufgabe |
|
|
|-------|---------|
|
|
| 1-2 | iOS SDK (Swift) |
|
|
| 3-4 | Android SDK (Kotlin) |
|
|
| 5-6 | Testing & Dokumentation |
|
|
|
|
**Deliverables:**
|
|
- iOS SDK v1.0.0
|
|
- Android SDK v1.0.0
|
|
- Beispiel-Apps
|
|
|
|
### Phase 3: Cross-Platform (4 Wochen)
|
|
|
|
| Woche | Aufgabe |
|
|
|-------|---------|
|
|
| 1-2 | Flutter SDK |
|
|
| 3-4 | React Native SDK |
|
|
|
|
**Deliverables:**
|
|
- Flutter SDK v1.0.0
|
|
- React Native SDK v1.0.0
|
|
|
|
### Phase 4: Enterprise Features (4 Wochen)
|
|
|
|
| Woche | Aufgabe |
|
|
|-------|---------|
|
|
| 1 | TCF 2.2 Integration |
|
|
| 2 | A/B Testing |
|
|
| 3 | Analytics Dashboard |
|
|
| 4 | White-Label UI |
|
|
|
|
**Deliverables:**
|
|
- TCF 2.2 Unterstützung
|
|
- Admin Dashboard
|
|
- A/B Testing Engine
|
|
|
|
### Phase 5: Integrationen (4 Wochen)
|
|
|
|
| Woche | Aufgabe |
|
|
|-------|---------|
|
|
| 1 | WordPress Plugin |
|
|
| 2 | Shopify App |
|
|
| 3 | GTM Template |
|
|
| 4 | Weitere CMS |
|
|
|
|
---
|
|
|
|
## 12. Anhang
|
|
|
|
### 12.1 Glossar
|
|
|
|
| Begriff | Beschreibung |
|
|
|---------|--------------|
|
|
| **CMP** | Consent Management Platform |
|
|
| **TCF** | IAB Transparency & Consent Framework |
|
|
| **DSGVO** | Datenschutz-Grundverordnung (EU 2016/679) |
|
|
| **TTDSG** | Telekommunikation-Telemedien-Datenschutz-Gesetz |
|
|
| **DSK** | Datenschutzkonferenz (Deutschland) |
|
|
| **PWA** | Progressive Web App |
|
|
| **ATT** | App Tracking Transparency (Apple) |
|
|
|
|
### 12.2 Referenzen
|
|
|
|
- [DSGVO Volltext](https://eur-lex.europa.eu/legal-content/DE/TXT/?uri=CELEX:32016R0679)
|
|
- [TTDSG Volltext](https://www.gesetze-im-internet.de/ttdsg/)
|
|
- [DSK Orientierungshilfe Telemedien](https://www.datenschutzkonferenz-online.de/media/oh/20211220_oh_telemedien.pdf)
|
|
- [IAB TCF 2.2 Specification](https://iabeurope.eu/tcf-2-0/)
|
|
- [EuGH Planet49 Urteil](https://curia.europa.eu/juris/liste.jsf?num=C-673/17)
|
|
- [BGH Cookie-Einwilligung II](https://www.bundesgerichtshof.de/SharedDocs/Pressemitteilungen/DE/2023/2023103.html)
|
|
- [WCAG 2.1](https://www.w3.org/TR/WCAG21/)
|
|
|
|
### 12.3 Lizenz
|
|
|
|
```
|
|
Copyright 2026 BreakPilot GmbH
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
```
|
|
|
|
### 12.4 Changelog
|
|
|
|
| Version | Datum | Änderungen |
|
|
|---------|-------|------------|
|
|
| 1.0.0 | 2026-02-04 | Initiale Spezifikation |
|
|
|
|
---
|
|
|
|
**Autor:** BreakPilot Engineering Team
|
|
**Review:** Datenschutzbeauftragter
|
|
**Freigabe:** Pending
|