Skip to content

structured-world/vue-privacy-worker

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

33 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Vue Privacy Worker

Cloudflare Worker companion for @structured-world/vue-privacy - GDPR consent storage in KV.

Features

  • Per-domain isolation (KV key prefix)
  • 365-day TTL for consent storage
  • Simple REST API (GET/POST)
  • CORS support with per-domain configuration
  • Rate limiting per IP address (configurable)
  • Consent versioning for privacy policy updates

API

Get Consent

GET /api/consent?id=<user_id>&version=<expected_version>

Parameters:

  • id (required): User unique identifier
  • version (optional): Expected consent version. If provided and doesn't match stored version, returns found: false to trigger re-consent

Response (consent found and version matches):

{
  "found": true,
  "consent": {
    "categories": {
      "analytics": true,
      "marketing": false,
      "functional": true
    },
    "timestamp": 1706198400000,
    "version": "1",
    "domain": "example.com",
    "updatedAt": "2024-01-25T12:00:00.000Z"
  }
}

Response (version mismatch - triggers re-consent):

{
  "found": false,
  "versionMismatch": true,
  "storedVersion": "1"  // The version stored in KV (null for legacy consents without version)
}

Store Consent

POST /api/consent
Content-Type: application/json

{
  "id": "user-unique-id",
  "categories": {
    "analytics": true,
    "marketing": false,
    "functional": true
  },
  "version": "1"
}

Response:

{
  "success": true,
  "id": "user-unique-id"
}

Geo Detection

GET /api/geo

Response:

{
  "isEU": true,
  "countryCode": "DE",
  "continent": "EU",
  "region": "Bavaria",
  "method": "worker"
}
Field Type Description
isEU boolean Whether visitor is in EU (for GDPR)
countryCode string | null ISO 3166-1 alpha-2 country code
continent string | null Continent code (EU, NA, AS, etc.)
region string | null Region/state name (for CCPA: California, Virginia, Colorado, Connecticut, Utah)
method string Always "worker"

KV Key Format

{domain}:{user_id}

Example: example.com:abc123-def456

Rate Limiting

The worker implements per-IP rate limiting to prevent abuse.

Default Limits

  • 100 requests per minute per IP address
  • Applies to /api/consent endpoints only (GET and POST)
  • /api/geo is not rate-limited

Customizing Limits

Override defaults via wrangler.toml variables:

[vars]
RATE_LIMIT_MAX_REQUESTS = "200"    # requests per window
RATE_LIMIT_WINDOW_SECONDS = "120"  # 2 minute window

Response Headers

Rate-limited endpoints (/api/consent) include rate limit headers:

Header Description
X-RateLimit-Limit Maximum requests allowed per window
X-RateLimit-Remaining Requests remaining in current window
X-RateLimit-Reset Unix timestamp when window resets

Rate Limit Exceeded (429)

When limit is exceeded:

{
  "error": "rate_limit_exceeded",
  "retryAfter": 45
}

Response headers include Retry-After with seconds until window resets.

KV Key Format for Rate Limits

Rate limit state is stored in KV with prefix rl::

rl:{ip_address}

This is a fixed-window rate limiter: the window resets based on an internal resetAt timestamp. The KV entry TTL is set to windowSeconds on each allowed request as a cleanup mechanism — entries auto-expire after the last allowed request.

Fail-open behavior: If KV operations fail (e.g., temporary unavailability), requests proceed without rate limiting. This prioritizes availability over strict enforcement. Rate limiting resumes automatically when KV recovers.

Consent Versioning

The worker supports consent versioning to handle privacy policy changes. When your privacy policy or cookie categories change, you can bump the consent version to invalidate existing consents and force users to re-consent.

How it works

  1. When storing consent via POST, include the version field (e.g., "1.0", "2.0")
  2. When retrieving consent via GET, pass the expected version query parameter
  3. If the stored version doesn't match the expected version, the response returns found: false with versionMismatch: true
  4. The client should show the consent banner again when version mismatch is detected

Version mismatch response

{
  "found": false,
  "versionMismatch": true,
  "storedVersion": "1.0"
}

Best practices

  • Use semantic versioning (e.g., "1.0", "1.1", "2.0")
  • Bump major version when cookie categories change
  • Bump minor version for privacy policy text changes
  • Store version in your app config and pass it to both GET and POST requests

Example: handling privacy policy update

// Your app config
const CONSENT_VERSION = "2.0"; // Bump when policy changes

// Check existing consent (relative URL works when served from same domain as the worker)
const response = await fetch(`/api/consent?id=${userId}&version=${CONSENT_VERSION}`);
const { found, versionMismatch } = await response.json();

if (!found) {
  if (versionMismatch) {
    console.log("Privacy policy updated, showing banner");
  }
  showConsentBanner();
}

// Store new consent
await fetch("/api/consent", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    id: userId,
    version: CONSENT_VERSION,
    categories: { analytics: true, marketing: false, functional: true }
  })
});

Self-Hosting

  1. Fork this repository
  2. Create KV namespace:
    wrangler kv:namespace create CONSENT_KV
  3. Update wrangler.toml with your KV namespace ID
  4. Add route for your domain:
    routes = [
      { pattern = "yourdomain.com/api/consent*", zone_name = "yourdomain.com" },
      { pattern = "yourdomain.com/api/geo", zone_name = "yourdomain.com" },
    ]
  5. Deploy:
    yarn deploy

Development

yarn install
yarn dev         # Local development
yarn test        # Run tests
yarn deploy      # Manual deploy

Status & Roadmap

Current (v1.2)

Feature Status
GET/POST consent API Done
Per-domain KV isolation Done
365-day TTL storage Done
CORS configuration Done
GitHub Actions deploy Done
Rate limiting Done
Consent versioning Done

Planned

Feature Description
vue-privacy integration Automatic sync with @structured-world/vue-privacy storage backend
Bulk export Admin API for compliance exports
Analytics events Optional consent analytics (opt-in rates)

Integration with @structured-world/vue-privacy

This worker is designed to work with the @structured-world/vue-privacy npm package for server-side consent storage.

Note: Integration with vue-privacy is planned but not yet implemented. Currently the worker provides a standalone REST API for consent storage.

License

Apache 2.0

About

Cloudflare Worker for GDPR consent storage (per-domain KV, 365 days TTL)

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors