npm package: @rivtor/consent
Description: GDPR Cookie Consent with GeoIP detection and encrypted consent logging
Author: Rivtor
Latest version: 1.1.0

# @rivtor/consent

**Production-ready GDPR Cookie Consent with GeoIP detection and encrypted consent logging**

## Features

- **GeoIP Detection**: Cloudflare headers and MaxMind GeoIP2 support
- **Consent State Machine**: no_consent_needed, awaiting_consent, consent_given, consent_revoked
- **Encrypted Logging**: 5-year retention with encrypted consent records
- **Pre-Built Components**: GDPR-compliant banner with Radix UI
- **Cookie Wall Prevention**: Architectural enforcement preventing cookie walls in EU
- **Category-Based Consent**: Essential, statistics, marketing, preferences

## Installation

```bash
npm install @rivtor/consent
# or
yarn add @rivtor/consent
# or
pnpm add @rivtor/consent
```

## Quick Start

### 1. Detect Consent Requirement

```typescript
import { detectConsentRequirement } from '@rivtor/consent/server';

// In your API route or middleware
const result = await detectConsentRequirement(
  request.headers.get('x-forwarded-for') || 'unknown',
  Object.fromEntries(request.headers)
);

console.log(result);
// {
//   country: 'DE',
//   isEu: true,
//   region: 'eu',
//   requiresConsent: true
// }
```

### 2. Wrap Your App

```tsx
'use client';
import { VocoConsentBanner } from '@rivtor/consent/react';
import { DEFAULT_CONSENT_CONFIG } from '@rivtor/consent/server';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <VocoConsentBanner
          config={DEFAULT_CONSENT_CONFIG}
          onConsentChange={(consent) => {
            console.log('User consent:', consent);
          }}
        />
      </body>
    </html>
  );
}
```

### 3. Use Consent Hook

```tsx
'use client';
import { useConsent } from '@rivtor/consent/react';

export function Analytics() {
  const { isCategoryAccepted, acceptCategory } = useConsent();

  useEffect(() => {
    if (isCategoryAccepted('statistics')) {
      // Load analytics
      initializeAnalytics();
    }
  }, [isCategoryAccepted]);

  return <div>Analytics Component</div>;
}
```

### 4. Server-Side Validation

```typescript
import { validateConsent, recordConsent } from '@rivtor/consent/server';

// Validate user consent before processing
const valid = await validateConsent({
  userId: 'user-123',
  sessionId: 'session-abc',
  requiredCategories: ['statistics', 'marketing'],
});

if (!valid.valid) {
  return Response.json({ error: 'Consent required' }, { status: 403 });
}

// Record consent decision
await recordConsent({
  userId: 'user-123',
  sessionId: 'session-abc',
  region: 'eu',
  state: 'consent_given',
  categoriesAccepted: ['essential', 'statistics'],
  consentVersion: '1.0',
  expiresAt: new Date(Date.now() + 5 * 365 * 24 * 60 * 60 * 1000),
});
```

## API Reference

### Server Functions

```typescript
import { consent } from '@rivtor/consent/server';

// Detect consent requirement from IP/headers
const result = await consent.detectConsentRequirement(ip, headers);

// Validate user consent
const valid = await consent.validateConsent({
  userId: 'user-123',
  sessionId: 'session-abc',
  requiredCategories: ['statistics', 'marketing'],
});

// Record consent decision
await consent.recordConsent({
  userId: 'user-123',
  sessionId: 'session-abc',
  region: 'eu',
  state: 'consent_given',
  categoriesAccepted: ['essential', 'statistics'],
  consentVersion: '1.0',
  expiresAt: new Date(Date.now() + 5 * 365 * 24 * 60 * 60 * 1000),
});

// Get user consent
const userConsent = await consent.getUserConsent('user-123');

// Revoke consent
await consent.revokeConsent('user-123', 'session-abc');

// Check if consent is needed
const needed = await consent.isConsentNeeded('user-123');

// Get consent statistics
const stats = await consent.getConsentStats(30);
```

### Client Functions

```typescript
import { createConsentManager } from '@rivtor/consent/client';

const manager = createConsentManager(DEFAULT_CONSENT_CONFIG);

// Check if consent is needed
if (manager.isConsentNeeded()) {
  // Show banner
}

// Grant consent for categories
await manager.grantConsent(['essential', 'statistics']);

// Revoke consent
await manager.revokeConsent();

// Check specific category
if (manager.isCategoryAccepted('statistics')) {
  // Load statistics
}

// Get current consent state
const state = manager.getState();

// Accept all categories
await manager.acceptAll();

// Accept only essential
await manager.acceptEssential();
```

### React Components

#### VocoConsentBanner

```tsx
<VocoConsentBanner
  config={DEFAULT_CONSENT_CONFIG}
  onConsentChange={(consent) => console.log('Consent:', consent)}
  position="bottom"
  style="default"
/>
```

#### useConsent Hook

```tsx
const {
  state,
  isConsentNeeded,
  isCategoryAccepted,
  acceptAll,
  acceptEssential,
  revoke,
  acceptCategory,
} = useConsent(DEFAULT_CONSENT_CONFIG);
```

## Configuration

```typescript
interface ConsentConfig {
  // Consent categories
  categories: {
    essential: {
      required: true;
      description: string;
    };
    statistics: {
      cookies: string[];
      description: string;
    };
    marketing: {
      cookies: string[];
      description: string;
    };
    preferences: {
      cookies: string[];
      description: string;
    };
  };

  // Banner settings
  banner: {
    title: string;
    description: string;
    acceptAllText: string;
    acceptEssentialText: string;
    privacyPolicyUrl: string;
    showDetails: boolean;
  };

  // Consent duration
  consentDuration: number; // days (default: 1825 = 5 years)

  // Current consent version
  consentVersion: string;
}
```

## Database Schema

```sql
CREATE TABLE consent_logs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID,
    session_id TEXT NOT NULL,
    ip_hash TEXT,
    region TEXT NOT NULL,
    state TEXT NOT NULL CHECK (state IN (
        'no_consent_needed',
        'awaiting_consent',
        'consent_given',
        'consent_revoked'
    )),
    categories_accepted TEXT[] NOT NULL DEFAULT '{}',
    consent_version TEXT NOT NULL,
    consented_at TIMESTAMPTZ NOT NULL,
    expires_at TIMESTAMPTZ NOT NULL,
    revoked_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Indexes for efficient queries
CREATE INDEX idx_consent_logs_user_id ON consent_logs(user_id);
CREATE INDEX idx_consent_logs_session_id ON consent_logs(session_id);
CREATE INDEX idx_consent_logs_expires_at ON consent_logs(expires_at);
CREATE INDEX idx_consent_logs_region ON consent_logs(region);
```

## Consent Categories

| Category | Required | Description |
|----------|----------|-------------|
| `essential` | Yes | Required for basic functionality |
| `statistics` | No | Analytics and performance monitoring |
| `marketing` | No | Advertising and tracking |
| `preferences` | No | User preferences and settings |

## License

MIT

---

Made with ❤️ by [Rivtor](https://rivtor.com)