# LiveComponents Security Guide

Comprehensive security guide for implementing and using LiveComponents with CSRF protection, rate limiting, and idempotency.

## Table of Contents

- [Overview](#overview)
- [CSRF Protection](#csrf-protection)
- [Rate Limiting](#rate-limiting)
- [Idempotency Keys](#idempotency-keys)
- [State Encryption](#state-encryption)
- [Combined Security](#combined-security)
- [Best Practices](#best-practices)
- [Testing Security](#testing-security)
- [Troubleshooting](#troubleshooting)

---

## Overview

LiveComponents implements a **defense-in-depth security model** with four core layers:

1. **CSRF Protection** - Prevents cross-site request forgery attacks
2. **Rate Limiting** - Protects against abuse and DDoS attacks
3. **Idempotency Keys** - Prevents duplicate operations from network issues
4. **State Encryption** - Protects sensitive state data with authenticated encryption

All security features are **enabled by default** and require no configuration for basic usage.

### Security Processing Order

```
Request → CSRF Validation → Rate Limit Check → Idempotency Check → Action Execution
```

If any layer fails, the request is rejected **before** reaching your component logic.

---

## CSRF Protection

### How It Works

Every LiveComponent action requires a valid CSRF token to execute. Tokens are:
- **Per-session**: One token per user session
- **Auto-rotated**: New token generated after each action
- **Short-lived**: Expires with the session

### Automatic Token Injection

The framework automatically injects CSRF tokens into your component templates:

```html
<!-- Your template -->
<form method="POST" data-lc-action="submitForm">
    <input type="text" name="email" />
    <button type="submit">Submit</button>
</form>

<!-- Automatically becomes -->
<form method="POST" data-lc-action="submitForm">
    <input type="hidden" name="_csrf_token" value="auto_generated_token" />
    <input type="text" name="email" />
    <button type="submit">Submit</button>
</form>
```

### JavaScript Integration

The LiveComponent JavaScript client automatically includes CSRF tokens:

```javascript
// Automatic - no configuration needed
liveComponent.executeAction('increment');

// The client automatically adds:
// { _csrf_token: 'session_token' }
```

### Manual Token Access

For custom forms or AJAX requests:

```php
// In your controller or component
$csrfToken = $this->session->getCsrfToken();

// In JavaScript
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;

fetch('/api/livecomponents/action', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Token': csrfToken
    },
    body: JSON.stringify({
        component_id: 'counter:main',
        action: 'increment'
    })
});
```

### CSRF Exceptions

```php
use App\Framework\Exception\Security\CsrfTokenMismatchException;

try {
    $this->handler->handleAction($component, 'increment', $params);
} catch (CsrfTokenMismatchException $e) {
    // Token is invalid, expired, or missing
    // User should refresh the page

    // Log security event
    $this->owaspLogger->logSecurityEvent(
        OWASPEventIdentifier::CSRF_ATTACK_DETECTED,
        $request
    );

    return new JsonResponse([
        'error' => 'Invalid security token. Please refresh the page.',
        'code' => 'CSRF_TOKEN_INVALID'
    ], Status::FORBIDDEN);
}
```

### Testing CSRF Protection

```php
use function Pest\LiveComponents\mountComponent;
use function Pest\LiveComponents\callAction;

it('requires valid CSRF token for actions', function () {
    $component = mountComponent('counter:test', ['count' => 0]);

    // Without CSRF token should fail
    expect(fn() => callAction($component, 'increment', [
        '_csrf_token' => 'invalid_token'
    ]))->toThrow(CsrfTokenMismatchException::class);
});

it('accepts valid CSRF token', function () {
    $component = mountComponent('counter:test', ['count' => 0]);
    $session = container()->get(Session::class);
    $csrfToken = $session->getCsrfToken();

    $result = callAction($component, 'increment', [
        '_csrf_token' => $csrfToken
    ]);

    expect($result['state']['count'])->toBe(1);
});
```

---

## Rate Limiting

### How It Works

Rate limiting prevents abuse by restricting the number of actions per time window:

- **Per-Client**: Tracked by IP address + User ID (if authenticated)
- **Per-Action**: Each action can have custom limits
- **Configurable**: Set via `#[Action]` attribute
- **Sliding Window**: Uses token bucket algorithm

### Default Limits

```php
// Framework defaults (can be overridden)
const DEFAULT_RATE_LIMIT = 60;  // requests
const DEFAULT_WINDOW = 60;       // seconds
```

### Custom Rate Limits per Action

```php
use App\Framework\LiveComponents\Attributes\Action;
use App\Framework\Core\ValueObjects\Duration;

#[LiveComponent('payment')]
final readonly class PaymentComponent implements LiveComponentContract
{
    // Strict limit for payment processing
    #[Action(rateLimit: 5, rateLimitWindow: 300)] // 5 per 5 minutes
    public function processPayment(PaymentRequest $request): ComponentData
    {
        // Process payment
    }

    // More lenient for viewing
    #[Action(rateLimit: 100, rateLimitWindow: 60)] // 100 per minute
    public function viewTransactions(): ComponentData
    {
        // View transactions
    }

    // No custom limit - uses framework default (60 per minute)
    #[Action]
    public function updateProfile(ProfileData $data): ComponentData
    {
        // Update profile
    }
}
```

### Rate Limit Responses

When rate limit is exceeded:

```json
{
    "error": "Rate limit exceeded",
    "code": "RATE_LIMIT_EXCEEDED",
    "retry_after": 45,
    "limit": 60,
    "window": 60
}
```

HTTP Status: **429 Too Many Requests**

Headers:
```
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1234567890
Retry-After: 45
```

### Handling Rate Limits in JavaScript

```javascript
liveComponent.on('error', (error) => {
    if (error.code === 'RATE_LIMIT_EXCEEDED') {
        const retryAfter = error.retry_after;

        // Show user-friendly message
        showNotification(
            `Too many requests. Please wait ${retryAfter} seconds.`,
            'warning'
        );

        // Optionally schedule retry
        setTimeout(() => {
            liveComponent.executeAction('retry');
        }, retryAfter * 1000);
    }
});
```

### Rate Limit Exceptions

```php
use App\Framework\Exception\Http\RateLimitExceededException;

try {
    $this->handler->handleAction($component, 'processPayment', $params);
} catch (RateLimitExceededException $e) {
    $retryAfter = $e->getRetryAfter(); // seconds

    // Log for monitoring
    $this->logger->warning('Rate limit exceeded', [
        'component' => $component->id->toString(),
        'action' => 'processPayment',
        'retry_after' => $retryAfter,
        'client_ip' => $request->server->getClientIp()
    ]);

    return new JsonResponse([
        'error' => 'Too many payment attempts',
        'retry_after' => $retryAfter
    ], Status::TOO_MANY_REQUESTS);
}
```

### Testing Rate Limiting

```php
it('enforces rate limits on actions', function () {
    $component = mountComponent('counter:test', ['count' => 0]);

    // Execute action multiple times rapidly
    for ($i = 0; $i < 10; $i++) {
        callAction($component, 'increment');
    }

    // 11th request should be rate limited
    expect(fn() => callAction($component, 'increment'))
        ->toThrow(RateLimitExceededException::class);
});

it('includes retry-after in rate limit response', function () {
    $component = mountComponent('counter:test', ['count' => 0]);

    // Exhaust rate limit
    for ($i = 0; $i < 10; $i++) {
        callAction($component, 'increment');
    }

    try {
        callAction($component, 'increment');
    } catch (RateLimitExceededException $e) {
        expect($e->getRetryAfter())->toBeGreaterThan(0);
    }
});
```

---

## Idempotency Keys

### How It Works

Idempotency keys prevent duplicate operations from:
- Network timeouts
- Double-clicks
- Browser back/forward
- Client-side retries

When the same idempotency key is used twice:
- First request: **Executes action**, caches result
- Subsequent requests: **Returns cached result**, no execution

### Automatic Idempotency

The LiveComponent JavaScript client automatically generates idempotency keys for critical actions:

```javascript
// Framework automatically adds idempotency key for state-changing actions
liveComponent.executeAction('processPayment', {
    amount: 100
});

// Internally becomes:
{
    action: 'processPayment',
    params: { amount: 100 },
    idempotency_key: 'auto_generated_uuid_v4'
}
```

### Manual Idempotency Keys

For custom control or server-side operations:

```php
use App\Framework\LiveComponents\Attributes\Action;

#[LiveComponent('order')]
final readonly class OrderComponent implements LiveComponentContract
{
    #[Action(idempotencyTTL: 3600)] // Cache for 1 hour
    public function createOrder(
        OrderRequest $request,
        ActionParameters $params
    ): ComponentData {
        // Idempotency key is automatically extracted and validated
        // If duplicate key: returns cached result without executing

        $order = $this->orderService->create($request);

        return $this->state->withOrder($order);
    }
}
```

### Custom Idempotency Key Generation

```javascript
// Generate consistent key for retry scenarios
const idempotencyKey = `order-${orderId}-${Date.now()}`;

liveComponent.executeAction('createOrder', {
    order_id: orderId,
    items: cartItems
}, {
    idempotency_key: idempotencyKey
});

// Retry with same key - will return cached result
setTimeout(() => {
    liveComponent.executeAction('createOrder', {
        order_id: orderId,
        items: cartItems
    }, {
        idempotency_key: idempotencyKey // Same key = cached result
    });
}, 5000);
```

### Idempotency Metadata

Response includes metadata about idempotency:

```json
{
    "html": "<div>Order created</div>",
    "state": { "order_id": "12345" },
    "idempotency": {
        "key": "order-12345-1234567890",
        "cached": false,
        "ttl": 3600,
        "created_at": "2024-01-15T10:30:00Z"
    }
}
```

On duplicate request:
```json
{
    "html": "<div>Order created</div>",
    "state": { "order_id": "12345" },
    "idempotency": {
        "key": "order-12345-1234567890",
        "cached": true,  // ← Indicates cached result
        "ttl": 3540,
        "created_at": "2024-01-15T10:30:00Z"
    }
}
```

### Idempotency TTL

Configure cache duration per action:

```php
// Short TTL for volatile operations
#[Action(idempotencyTTL: 300)] // 5 minutes
public function updateCart(): ComponentData

// Long TTL for critical operations
#[Action(idempotencyTTL: 86400)] // 24 hours
public function processPayment(): ComponentData

// No idempotency (default: 3600 seconds / 1 hour)
#[Action]
public function viewProfile(): ComponentData
```

### Testing Idempotency

```php
it('prevents duplicate action execution with same idempotency key', function () {
    $component = mountComponent('counter:test', ['count' => 0]);
    $idempotencyKey = 'test-key-' . uniqid();

    // First execution
    $result1 = callAction($component, 'increment', [
        'idempotency_key' => $idempotencyKey
    ]);

    expect($result1['state']['count'])->toBe(1);

    // Second execution with same key should return cached result
    $result2 = callAction($result1, 'increment', [
        'idempotency_key' => $idempotencyKey
    ]);

    // Count should still be 1 (not 2)
    expect($result2['state']['count'])->toBe(1);
    expect($result2['idempotency']['cached'])->toBeTrue();
});

it('allows different actions with different idempotency keys', function () {
    $component = mountComponent('counter:test', ['count' => 0]);

    $result1 = callAction($component, 'increment', [
        'idempotency_key' => 'key-1'
    ]);

    $result2 = callAction($result1, 'increment', [
        'idempotency_key' => 'key-2'
    ]);

    expect($result2['state']['count'])->toBe(2);
});
```

---

## State Encryption

### Overview

Sensitive LiveComponent state data is automatically encrypted using authenticated encryption (XSalsa20-Poly1305 via libsodium) to protect against:
- State tampering
- Unauthorized access to sensitive data
- Replay attacks with modified state
- Side-channel attacks

### When to Use State Encryption

Encrypt state when it contains:
- **Sensitive User Data**: Email addresses, phone numbers, personal information
- **Session Tokens**: Authentication tokens, API keys, session identifiers
- **Financial Data**: Payment information, account balances, transaction details
- **Private Business Data**: Internal IDs, unpublished content, proprietary information

### Automatic Encryption via Transformer

```php
use App\Framework\StateManagement\CacheBasedStateManager;
use App\Framework\StateManagement\EncryptionTransformer;
use App\Framework\LiveComponents\Serialization\EncryptedStateSerializer;
use App\Framework\LiveComponents\Serialization\StateEncryptor;

// 1. Setup encryption components
$encryptionKey = $vault->get('state_encryption_key'); // 32-byte key from Vault
$encryptor = new StateEncryptor($encryptionKey, $crypto, $random);
$serializer = new EncryptedStateSerializer($encryptor);

// 2. Add EncryptionTransformer to StateManager pipeline
$encryptionTransformer = new EncryptionTransformer($serializer);

$stateManager = new CacheBasedStateManager(
    $cache,
    transformers: [$encryptionTransformer] // Automatic encryption/decryption
);

// 3. Use normally - encryption happens transparently
$stateManager->store($componentId, $userState);
$state = $stateManager->retrieve($componentId, UserState::class);
```

### Creating Encryptable State

```php
use App\Framework\StateManagement\SerializableState;

final readonly class UserProfileState implements SerializableState
{
    public function __construct(
        public string $userId,
        public Email $email,
        public string $sessionToken,     // Sensitive - will be encrypted
        public array $preferences        // Sensitive - will be encrypted
    ) {}

    public function toArray(): array
    {
        return [
            'user_id' => $this->userId,
            'email' => $this->email->value,
            'session_token' => $this->sessionToken,
            'preferences' => $this->preferences,
        ];
    }

    public static function fromArray(array $data): self
    {
        return new self(
            userId: $data['user_id'],
            email: new Email($data['email']),
            sessionToken: $data['session_token'],
            preferences: $data['preferences']
        );
    }
}
```

### Security Features

**Authenticated Encryption**:
- **Algorithm**: XSalsa20-Poly1305 (via `sodium_crypto_secretbox`)
- **Key Size**: 256-bit (32 bytes)
- **Nonce**: 24 bytes, unique per encryption operation
- **MAC**: Poly1305 authentication tag automatically included
- **Tampering Detection**: MAC verification on every decryption

**Encrypted Data Format**:
```
[Version:1][Nonce:24 bytes][Ciphertext + MAC]
           └─ Base64 encoded for cache storage
```

### Error Handling

```php
use App\Framework\LiveComponents\Exceptions\StateEncryptionException;

try {
    $state = $stateManager->retrieve($componentId, UserState::class);
} catch (StateEncryptionException $e) {
    // Encryption/decryption failed
    // Possible causes:
    // - Wrong encryption key
    // - Corrupted encrypted data
    // - MAC verification failed (tampering detected)

    // SECURITY: Never expose encryption details to client
    error_log("State decryption failed: " . $e->getMessage());

    // Regenerate state from source or show error
    throw new \RuntimeException('Session invalid');
}
```

### Key Management

**Generating Encryption Keys**:
```php
use App\Framework\Random\SecureRandomGenerator;

$random = new SecureRandomGenerator();
$encryptionKey = $random->bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); // 32 bytes

// Store in Vault (encrypted at rest)
$vault->store('state_encryption_key', $encryptionKey);
```

**Key Storage Best Practices**:
- ✅ Store in Vault (encrypted at rest)
- ✅ Separate keys per environment (dev/staging/prod)
- ✅ Rotate keys quarterly
- ❌ DO NOT commit keys to version control
- ❌ DO NOT store in .env files
- ❌ DO NOT store in plain text database

### Performance

- **Encryption Overhead**: ~1-2ms per operation
- **Recommended**: Use for sensitive data only
- **Caching**: Encrypted states cached normally (encryption is transparent)
- **TTL**: Consider shorter TTL for encrypted states (balance security/performance)

### Testing Encrypted State

```php
it('encrypts and decrypts user state correctly', function () {
    $userState = new UserProfileState(
        userId: 'user123',
        email: new Email('test@example.com'),
        sessionToken: 'secret-token-xyz',
        preferences: ['theme' => 'dark']
    );

    // Store with encryption
    $stateManager->store($componentId, $userState);

    // Retrieve and decrypt
    $retrieved = $stateManager->retrieve($componentId, UserProfileState::class);

    expect($retrieved->sessionToken)->toBe('secret-token-xyz');
    expect($retrieved->preferences)->toBe(['theme' => 'dark']);
});

it('detects state tampering via MAC verification', function () {
    $stateManager->store($componentId, $userState);

    // Tamper with encrypted state in cache
    $encrypted = $cache->get($componentId);
    $tampered = substr($encrypted, 0, -5) . 'XXXXX';
    $cache->set($componentId, $tampered);

    // Should throw StateEncryptionException
    $stateManager->retrieve($componentId, UserProfileState::class);
})->throws(StateEncryptionException::class, 'MAC verification failed');
```

### Migration to Encrypted State

```php
// Step 1: Add EncryptionTransformer to existing StateManager
$stateManager = new CacheBasedStateManager(
    $cache,
    transformers: [
        $encryptionTransformer  // Add encryption transformer
    ]
);

// Step 2: Existing unencrypted states will be:
// - Read normally (transformer detects unencrypted format)
// - Re-encrypted on next update

// Step 3: Monitor for encryption errors during transition
// Old states may fail decryption - regenerate from source
```

**See Also**:
- [Security Patterns - State Encryption](/docs/claude/security-patterns.md#state-encryption-for-livecomponents)
- [StateEncryptor API Reference](../Serialization/StateEncryptor.php)
- [EncryptionTransformer API Reference](../../StateManagement/EncryptionTransformer.php)

---

## Combined Security

All four security layers work together:

### Validation Order

1. **CSRF Token** - Validates authenticity
2. **Rate Limit** - Checks request frequency
3. **Idempotency Key** - Prevents duplicates
4. **Action Execution** - Runs your logic
5. **State Encryption** - Encrypts sensitive state before storage

```php
// Example: Payment processing with all security layers
#[LiveComponent('payment')]
final readonly class PaymentComponent implements LiveComponentContract
{
    // Sensitive state with encryption
    public function __construct(
        private StateManager $stateManager  // Configured with EncryptionTransformer
    ) {}

    #[Action(
        rateLimit: 5,           // Max 5 payment attempts
        rateLimitWindow: 300,   // Per 5 minutes
        idempotencyTTL: 86400   // Cache result for 24 hours
    )]
    public function processPayment(
        PaymentRequest $request,
        ActionParameters $params
    ): ComponentData {
        // Security Layers Applied (in order):
        // 1. CSRF: Already validated by framework
        // 2. Rate Limit: Already checked by framework
        // 3. Idempotency: Cached result returned if duplicate key

        // 4. Action Execution: Only if all security checks pass
        $payment = $this->paymentGateway->charge($request);

        // 5. State Encryption: Sensitive payment data automatically encrypted
        return $this->state->withPayment($payment);
        // ↑ StateManager encrypts this state before cache storage
    }
}
```

### Security Event Flow

```
1. Client sends request with CSRF token + idempotency key
2. Framework validates CSRF token
   ├─ Invalid → 403 Forbidden (CsrfTokenMismatchException)
   └─ Valid → Continue
3. Framework checks rate limit
   ├─ Exceeded → 429 Too Many Requests (RateLimitExceededException)
   └─ OK → Continue
4. Framework checks idempotency key
   ├─ Duplicate → Return cached result (no execution)
   └─ New → Continue
5. Execute action and cache result
6. Return response + new CSRF token
```

### Testing Combined Security

```php
it('enforces all security layers together', function () {
    $component = mountComponent('payment:test', ['balance' => 1000]);
    $session = container()->get(Session::class);
    $csrfToken = $session->getCsrfToken();
    $idempotencyKey = 'payment-' . uniqid();

    // Valid request with all security features
    $result = callAction($component, 'processPayment', [
        '_csrf_token' => $csrfToken,
        'idempotency_key' => $idempotencyKey,
        'amount' => 100
    ]);

    expect($result['state']['balance'])->toBe(900);

    // Retry with same idempotency key but new CSRF token
    $newCsrfToken = $session->getCsrfToken();

    $result2 = callAction($result, 'processPayment', [
        '_csrf_token' => $newCsrfToken,
        'idempotency_key' => $idempotencyKey,
        'amount' => 100
    ]);

    // Should return cached result due to idempotency
    expect($result2['state']['balance'])->toBe(900); // Still 900, not 800
    expect($result2['idempotency']['cached'])->toBeTrue();
});
```

---

## Best Practices

### 1. Always Use Idempotency for Critical Operations

```php
// ✅ Good - Payments are idempotent
#[Action(idempotencyTTL: 86400)]
public function processPayment(PaymentRequest $request): ComponentData

// ✅ Good - Order creation is idempotent
#[Action(idempotencyTTL: 3600)]
public function createOrder(OrderRequest $request): ComponentData

// ⚠️ Warning - View operations don't need idempotency
#[Action] // No idempotency needed for reads
public function viewProfile(): ComponentData
```

### 2. Set Appropriate Rate Limits

```php
// ✅ Good - Strict limits for critical actions
#[Action(rateLimit: 5, rateLimitWindow: 300)] // 5 per 5 min
public function processPayment(): ComponentData

// ✅ Good - Lenient for viewing
#[Action(rateLimit: 100, rateLimitWindow: 60)] // 100 per min
public function viewDashboard(): ComponentData

// ❌ Bad - Too lenient for critical action
#[Action(rateLimit: 1000, rateLimitWindow: 60)]
public function deleteAccount(): ComponentData
```

### 3. Handle Security Exceptions Gracefully

```php
try {
    $result = $this->handler->handleAction($component, $action, $params);
} catch (CsrfTokenMismatchException $e) {
    // User-friendly message
    return new JsonResponse([
        'error' => 'Your session has expired. Please refresh the page.',
        'code' => 'SESSION_EXPIRED',
        'action' => 'REFRESH_PAGE'
    ], Status::FORBIDDEN);
} catch (RateLimitExceededException $e) {
    // Include retry information
    return new JsonResponse([
        'error' => 'Too many requests. Please try again later.',
        'code' => 'RATE_LIMIT_EXCEEDED',
        'retry_after' => $e->getRetryAfter()
    ], Status::TOO_MANY_REQUESTS);
}
```

### 4. Log Security Events

```php
use App\Framework\Security\OWASPSecurityLogger;
use App\Framework\Security\OWASPEventIdentifier;

// Log failed CSRF attempts
$this->owaspLogger->logSecurityEvent(
    OWASPEventIdentifier::CSRF_ATTACK_DETECTED,
    $request,
    context: [
        'component' => $componentId,
        'action' => $actionName,
        'expected_token' => substr($expectedToken, 0, 8) . '...',
        'received_token' => substr($receivedToken, 0, 8) . '...'
    ]
);

// Log rate limit violations
$this->owaspLogger->logSecurityEvent(
    OWASPEventIdentifier::RATE_LIMIT_EXCEEDED,
    $request,
    context: [
        'component' => $componentId,
        'action' => $actionName,
        'limit' => $limit,
        'window' => $window,
        'retry_after' => $retryAfter
    ]
);
```

### 5. Test All Security Layers

```php
// Test suite should cover:
it('validates CSRF tokens', function () { /* ... */ });
it('enforces rate limits', function () { /* ... */ });
it('prevents duplicate operations', function () { /* ... */ });
it('combines all security layers', function () { /* ... */ });
it('handles security exceptions gracefully', function () { /* ... */ });
```

---

## Testing Security

Complete test examples in `tests/Feature/LiveComponents/SecurityTest.php`:

```php
describe('LiveComponent Security', function () {
    describe('CSRF Protection', function () {
        // ... CSRF tests
    });

    describe('Rate Limiting', function () {
        // ... Rate limit tests
    });

    describe('Idempotency Keys', function () {
        // ... Idempotency tests
    });

    describe('Combined Security Features', function () {
        // ... Integration tests
    });
});
```

Run security tests:
```bash
./vendor/bin/pest tests/Feature/LiveComponents/SecurityTest.php
```

---

## Troubleshooting

### CSRF Token Issues

**Problem**: "CSRF token mismatch" errors

**Causes**:
- Session expired
- Multiple browser tabs
- Cookie issues (SameSite, Secure)
- Cache/CDN stripping tokens

**Solutions**:
```php
// 1. Check session configuration
$sessionConfig = [
    'cookie_lifetime' => 3600,
    'cookie_secure' => true,      // HTTPS only
    'cookie_samesite' => 'Strict', // Prevent CSRF
    'cookie_httponly' => true      // No JavaScript access
];

// 2. Add token refresh mechanism
public function refreshCsrfToken(): JsonResponse
{
    $newToken = $this->session->regenerateCsrfToken();

    return new JsonResponse([
        'csrf_token' => $newToken
    ]);
}

// 3. Client-side token refresh
liveComponent.on('csrf_error', async () => {
    const response = await fetch('/api/csrf/refresh');
    const { csrf_token } = await response.json();

    // Retry original request with new token
    liveComponent.setCsrfToken(csrf_token);
});
```

### Rate Limit Issues

**Problem**: Legitimate users hitting rate limits

**Causes**:
- Shared IP addresses (NAT, VPN)
- Aggressive polling/refreshing
- Automated testing without cleanup
- Incorrect rate limit configuration

**Solutions**:
```php
// 1. Use user-based limits for authenticated users
$clientId = $this->auth->check()
    ? ClientIdentifier::forUser($this->auth->id())
    : ClientIdentifier::forIp($request->server->getClientIp());

// 2. Different limits for authenticated vs anonymous
#[Action(
    rateLimit: 100,        // Authenticated users
    rateLimitWindow: 60,
    anonymousLimit: 20     // Anonymous users
)]

// 3. Whitelist trusted IPs
$trustedIps = ['10.0.0.0/8', '172.16.0.0/12'];
if ($this->ipWhitelist->isTrusted($clientIp)) {
    // Skip rate limiting
}

// 4. Clear rate limits in tests
afterEach(function () {
    $cache = container()->get(Cache::class);
    $cache->clear(); // Reset rate limit counters
});
```

### Idempotency Issues

**Problem**: Cached results returned when they shouldn't be

**Causes**:
- TTL too long
- Key collision
- Cache not cleared after errors
- Stale cached errors

**Solutions**:
```php
// 1. Shorter TTL for volatile data
#[Action(idempotencyTTL: 300)] // 5 minutes instead of 1 hour

// 2. Include more context in key
$idempotencyKey = sprintf(
    'order-%s-user-%s-%s',
    $orderId,
    $userId,
    $timestamp
);

// 3. Clear cache on errors
try {
    $result = $this->processOrder($request);
} catch (\Exception $e) {
    // Don't cache errors
    $this->idempotencyService->forget($idempotencyKey);
    throw $e;
}

// 4. Add version to key for breaking changes
$idempotencyKey = "v2-order-{$orderId}"; // v1 keys ignored
```

---

## Security Checklist

Before deploying to production:

- [ ] CSRF protection enabled (default: ✅)
- [ ] Rate limits configured per action
- [ ] Idempotency TTL appropriate for each action
- [ ] Security exceptions logged (OWASP events)
- [ ] HTTPS enforced (CSRF tokens require secure cookies)
- [ ] Session configuration hardened (Secure, HttpOnly, SameSite)
- [ ] Security tests passing
- [ ] Monitoring/alerting for security events
- [ ] Error messages user-friendly (no internal details)
- [ ] Rate limit responses include retry-after

---

## Additional Resources

- **Framework Documentation**: `/docs/claude/security-patterns.md`
- **OWASP Security Events**: `/docs/claude/security-patterns.md#owasp-event-integration`
- **Testing Guide**: `tests/Feature/LiveComponents/SecurityTest.php`
- **Example Components**: `src/Application/LiveComponents/Counter/CounterComponent.php`

---

## Summary

LiveComponents provides **production-ready security** out of the box:

✅ **CSRF Protection** - Automatic token injection and validation
✅ **Rate Limiting** - Configurable per-action limits
✅ **Idempotency Keys** - Prevents duplicate operations
✅ **Defense in Depth** - Multiple security layers
✅ **Framework Integration** - OWASP logging, monitoring
✅ **Developer-Friendly** - Automatic, minimal configuration

All security features are **enabled by default** and require **zero configuration** for basic usage. Advanced scenarios allow fine-grained control via `#[Action]` attributes.
