# CHIPS Cookies (Cookies Having Independent Partitioned State)

CHIPS Cookie-Unterstützung im Custom PHP Framework für datenschutzfreundliche Third-Party-Cookies.

## Übersicht

CHIPS (Cookies Having Independent Partitioned State) ist ein Browser-Security-Feature, das Third-Party-Cookies **pro Top-Level-Site partitioniert**, um Cross-Site-Tracking zu verhindern.

**Browser-Support**:
- Chrome 114+
- Edge 114+
- Opera 100+
- Safari 16.4+ (partial)

## Was ist CHIPS?

### Problem: Traditional Third-Party Cookies

```
Widget embedded on example.com: cookie=user123
Same widget on test.com: cookie=user123 (SAME COOKIE!)
→ Cross-Site-Tracking möglich
```

### Lösung: CHIPS Partitioned Cookies

```
Widget embedded on example.com: cookie=user123_site_A
Same widget on test.com: cookie=user456_site_B (DIFFERENT!)
→ Kein Cross-Site-Tracking
```

## Requirements

CHIPS Cookies erfordern:
1. **Secure=true** (HTTPS only)
2. **SameSite=None** (Third-Party context)
3. **Partitioned** Attribut

## Verwendung

### CookieService Injection

```php
final readonly class EmbeddedWidgetController
{
    public function __construct(
        private CookieService $cookieService
    ) {}
}
```

### Standard Cookie vs. CHIPS Cookie

```php
// ❌ Standard Third-Party Cookie (tracking möglich)
$cookie = $this->cookieService->create(
    name: 'analytics',
    value: 'user123',
    sameSite: SameSite::None,
    secure: true
);

// ✅ CHIPS Partitioned Cookie (tracking verhindert)
$cookie = $this->cookieService->createPartitioned(
    name: 'analytics',
    value: 'user123'
);
```

### Use Case 1: Embedded Chat Widget

```php
#[Route(path: '/widget/chat/init', method: Method::GET)]
public function initChatWidget(HttpRequest $request): JsonResponse
{
    $session = $this->chatService->createSession();

    // Partitioned session cookie - different per embedding site
    $sessionCookie = $this->cookieService->createPartitionedSession(
        name: 'chat_session',
        value: $session->id,
        httpOnly: true
    );

    // Partitioned state cookie - JavaScript accessible
    $stateCookie = $this->cookieService->createPartitioned(
        name: 'chat_state',
        value: json_encode(['minimized' => false]),
        maxAge: Duration::fromDays(7),
        httpOnly: false  // JS needs access
    );

    return (new JsonResponse(['session_id' => $session->id]))
        ->withCookies($sessionCookie, $stateCookie);
}
```

### Use Case 2: Third-Party Analytics

```php
#[Route(path: '/analytics/track', method: Method::POST)]
public function track(HttpRequest $request): JsonResponse
{
    $userId = $request->cookies->get('_analytics');

    if (!$userId) {
        // Create partitioned analytics cookie
        $userId = $this->generateUserId();

        $cookie = $this->cookieService->createAnalyticsCookie(
            userId: $userId,
            maxAge: Duration::fromDays(365)
        );

        return (new JsonResponse(['tracked' => true]))
            ->withCookie($cookie);
    }

    $this->analyticsService->track($userId, $request->parsedBody);
    return new JsonResponse(['tracked' => true]);
}
```

### Use Case 3: Payment Provider Embed

```php
#[Route(path: '/payment/stripe/embed', method: Method::GET)]
public function embedPaymentForm(HttpRequest $request): ViewResult
{
    $checkoutSession = $this->stripeService->createCheckoutSession();

    // Partitioned - separate state per merchant site
    $sessionCookie = $this->cookieService->createPartitioned(
        name: 'stripe_checkout',
        value: $checkoutSession->id,
        maxAge: Duration::fromMinutes(30),
        httpOnly: true
    );

    return (new ViewResult('payment/stripe-embed', [
        'session' => $checkoutSession
    ]))->withCookie($sessionCookie);
}
```

## CookieService Factory Methods

### Standard Cookies

```php
// Basic cookie
$cookie = $this->cookieService->create(
    name: 'session',
    value: 'abc123',
    maxAge: Duration::fromHours(1),
    path: '/',
    domain: null,
    secure: null,  // Auto true in production
    httpOnly: true,
    sameSite: SameSite::Lax
);

// Session cookie (no expiration)
$cookie = $this->cookieService->createSessionCookie(
    name: 'session_id',
    value: 'xyz789',
    httpOnly: true,
    sameSite: SameSite::Lax
);

// Remember-me cookie (30 days default)
$cookie = $this->cookieService->createRememberMeCookie(
    value: 'token123',
    maxAge: Duration::fromDays(30)  // Optional
);

// Delete cookie
$cookie = $this->cookieService->delete('session', path: '/');
```

### CHIPS Partitioned Cookies

```php
// Partitioned cookie
$cookie = $this->cookieService->createPartitioned(
    name: 'widget',
    value: 'state123',
    maxAge: Duration::fromHours(1),
    path: '/',
    domain: null,
    httpOnly: true
);

// Partitioned session cookie
$cookie = $this->cookieService->createPartitionedSession(
    name: 'widget_session',
    value: 'session123',
    httpOnly: true
);

// Partitioned analytics cookie (JS accessible)
$cookie = $this->cookieService->createAnalyticsCookie(
    userId: 'user123',
    maxAge: Duration::fromDays(365)
);

// Partitioned widget state cookie
$cookie = $this->cookieService->createWidgetStateCookie(
    widgetId: 'chat',
    state: json_encode(['minimized' => false]),
    maxAge: Duration::fromDays(30)
);
```

## Response Integration

### Single Cookie

```php
return (new JsonResponse(['success' => true]))
    ->withCookie($cookie);
```

### Multiple Cookies

```php
return (new JsonResponse(['success' => true]))
    ->withCookies($sessionCookie, $stateCookie);
```

## Validation & Helpers

```php
// Validate cookie size (4KB browser limit)
if (!$this->cookieService->validateSize($cookie)) {
    throw new \RuntimeException('Cookie exceeds size limit');
}

// Validate cookie name
if (!$this->cookieService->isValidName('user-token')) {
    throw new \InvalidArgumentException('Invalid cookie name');
}
```

## Cookie Header Output

### Standard Cookie

```http
Set-Cookie: session=abc123; Path=/; Secure; HttpOnly; SameSite=Lax
```

### CHIPS Partitioned Cookie

```http
Set-Cookie: widget=state123; Path=/; Secure; HttpOnly; SameSite=None; Partitioned
```

## Wann CHIPS verwenden?

### ✅ Use CHIPS für:

1. **Embedded Widgets** - Chat, Support, Social Media embeds
2. **Third-Party Analytics** - Analytics in iframes
3. **Payment Provider Embeds** - Stripe, PayPal checkout forms
4. **OAuth Flows in iframes** - Third-party authentication
5. **Cross-Origin Video Players** - Embedded media players

### ❌ NICHT CHIPS für:

1. **First-Party Session Cookies** - SameSite=Lax reicht
2. **Authentication Cookies** - Nur same-site verwenden
3. **CSRF Tokens** - SameSite=Strict empfohlen
4. **Standard E-Commerce** - First-party cookies ausreichend

## Security Best Practices

### 1. Production Environment

```php
// Secure flag wird in production automatisch gesetzt
$prodEnv = new Environment([EnvKey::APP_ENV->value => 'production']);
$service = new CookieService($prodEnv);

$cookie = $service->create('session', 'value');
// → Secure flag automatisch true in production
```

### 2. HTTPS Required

CHIPS Cookies erfordern HTTPS:
```php
// ✅ Wird in Cookie-Konstruktor validiert
$cookie = new Cookie(
    name: 'widget',
    value: 'data',
    secure: true,           // Required
    sameSite: SameSite::None, // Required
    partitioned: true
);

// ❌ Throws InvalidArgumentException
$cookie = new Cookie(
    name: 'widget',
    value: 'data',
    secure: false,  // Invalid!
    partitioned: true
);
```

### 3. HttpOnly für Session Cookies

```php
// ✅ HttpOnly für session cookies
$session = $this->cookieService->createPartitionedSession(
    name: 'session',
    value: 'secret',
    httpOnly: true  // XSS protection
);

// ❌ Nur wenn JS-Zugriff WIRKLICH notwendig
$widget = $this->cookieService->createPartitioned(
    name: 'widget_state',
    value: json_encode(['data']),
    httpOnly: false  // JS accessible
);
```

## Testing

```php
describe('CHIPS Cookie Integration', function () {
    it('creates partitioned cookie for widget', function () {
        $service = new CookieService($this->environment);

        $cookie = $service->createPartitioned(
            name: 'widget',
            value: 'state123'
        );

        expect($cookie->partitioned)->toBeTrue();
        expect($cookie->secure)->toBeTrue();
        expect($cookie->sameSite)->toBe(SameSite::None);

        $header = $cookie->toHeaderString();
        expect($header)->toContain('Partitioned');
    });
});
```

## Framework Integration

### DI Container Initializer

```php
final readonly class CookieServiceInitializer
{
    #[Initializer]
    public function __invoke(Container $container): CookieService
    {
        return new CookieService(
            environment: $container->get(Environment::class)
        );
    }
}
```

### Response Trait (Future Enhancement)

```php
trait ResponseWithCookies
{
    private array $cookies = [];

    public function withCookie(Cookie $cookie): self
    {
        $new = clone $this;
        $new->cookies[] = $cookie;
        return $new;
    }

    public function withCookies(Cookie ...$cookies): self
    {
        $new = clone $this;
        $new->cookies = array_merge($new->cookies, $cookies);
        return $new;
    }
}
```

## Migration von Standard Third-Party Cookies

### Before (Traditional)

```php
$cookie = new Cookie(
    name: 'analytics',
    value: 'user123',
    secure: true,
    sameSite: SameSite::None
);
// Problem: Cross-site tracking möglich
```

### After (CHIPS)

```php
$cookie = $this->cookieService->createPartitioned(
    name: 'analytics',
    value: 'user123'
);
// Benefit: Privacy-friendly, kein Cross-site tracking
```

## Browser Compatibility Check (Future)

```php
final readonly class ChipsSupportDetector
{
    public function supportsChips(UserAgent $userAgent): bool
    {
        // Chrome/Edge 114+, Safari 16.4+, Opera 100+
        return $this->detectBrowserVersion($userAgent);
    }

    public function getFallbackStrategy(): CookieStrategy
    {
        // First-party only, localStorage, etc.
        return CookieStrategy::FIRST_PARTY_ONLY;
    }
}
```

## Troubleshooting

### Cookie wird nicht gesetzt

1. **HTTPS fehlt**: CHIPS erfordert Secure=true → HTTPS
2. **SameSite≠None**: Muss `SameSite::None` sein
3. **Browser-Support**: Prüfe Browser-Version
4. **Cookie-Size**: Max 4KB - nutze `validateSize()`

### Cross-Site funktioniert nicht

1. **Partitioned fehlt**: Nutze `createPartitioned()` statt `create()`
2. **Domain-Mismatch**: Domain-Attribut korrekt setzen
3. **Path-Mismatch**: Path muss übereinstimmen

## Performance Considerations

- **Cookie-Size**: CHIPS Cookies zählen zum 4KB-Limit
- **Anzahl Cookies**: Browser-Limit beachten (typically 50-180)
- **Expiration**: Alte Cookies regelmäßig löschen

## Zusammenfassung

CHIPS Cookies bieten:
✅ **Privacy-Friendly** - Kein Cross-Site-Tracking
✅ **Framework-Compliant** - Value Objects, Readonly, Type Safety
✅ **Developer-Friendly** - Einfache Factory Methods
✅ **Production-Ready** - Automatic Secure Flag, Validation
✅ **Well-Tested** - Comprehensive Pest Test Suite

Das Framework unterstützt CHIPS nativ für moderne, datenschutzfreundliche Third-Party-Integrationen.
