# Security

## HMAC Verification

To ensure webhooks are authentically from Joy Loyalty, each webhook request includes an `X-Joy-Loyalty-Hmac-Sha256` header containing the HMAC-SHA256 signature.

### Verification process

1. Get the raw body of the incoming webhook request
2. Calculate HMAC-SHA256 using your shop's secret key
3. Compare the calculated hash with the header value

### Verification examples

#### Node.js

```javascript
const crypto = require('crypto');

function verifyWebhook(rawBody, hmacHeader, secretKey) {
  const calculatedHmac = crypto
    .createHmac('sha256', secretKey)
    .update(rawBody, 'utf8')
    .digest('base64');

  return crypto.timingSafeEqual(
    Buffer.from(calculatedHmac),
    Buffer.from(hmacHeader)
  );
}

// Express middleware
app.post('/webhook/points', express.raw({type: 'application/json'}), (req, res) => {
  const hmac = req.get('X-Joy-Loyalty-Hmac-Sha256');
  const isValid = verifyWebhook(req.body, hmac, process.env.JOY_SECRET_KEY);

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  const payload = JSON.parse(req.body);
  // Process webhook...

  res.status(200).send('OK');
});
```

#### Python

```python
import hmac
import hashlib
import base64

def verify_webhook(raw_body, hmac_header, secret_key):
    calculated_hmac = base64.b64encode(
        hmac.new(
            secret_key.encode('utf-8'),
            raw_body,
            hashlib.sha256
        ).digest()
    ).decode()
    
    return hmac.compare_digest(calculated_hmac, hmac_header)

# Flask example
@app.route('/webhook/points', methods=['POST'])
def handle_webhook():
    hmac_header = request.headers.get('X-Joy-Loyalty-Hmac-Sha256')
    if not verify_webhook(request.data, hmac_header, SECRET_KEY):
        return 'Invalid signature', 401
    
    payload = request.get_json()
    # Process webhook...
    
    return 'OK', 200
```

#### PHP

```php
<?php
function verifyWebhook($rawBody, $hmacHeader, $secretKey) {
    $calculatedHmac = base64_encode(hash_hmac('sha256', $rawBody, $secretKey, true));
    return hash_equals($calculatedHmac, $hmacHeader);
}

// Usage
$rawBody = file_get_contents('php://input');
$hmacHeader = $_SERVER['HTTP_X_JOY_LOYALTY_HMAC_SHA256'] ?? '';

if (!verifyWebhook($rawBody, $hmacHeader, $secretKey)) {
    http_response_code(401);
    exit('Invalid signature');
}

$payload = json_decode($rawBody, true);
// Process webhook...

http_response_code(200);
echo 'OK';
?>
```

## Security considerations

### HMAC verification best practices

* **Always verify** HMAC signatures to ensure webhook authenticity
* **Use timing-safe comparison** functions to prevent timing attacks
* **Store secret keys securely** using environment variables or secure key management

### HTTPS requirements

* **Webhook endpoints must use HTTPS** for secure data transmission
* **Use valid SSL certificates** - self-signed certificates are not supported
* **Consider certificate pinning** for additional security

### IP whitelisting

Consider implementing IP whitelisting for additional security:

```javascript
const allowedIPs = ['webhook-ip-range']; // Update with actual IP ranges

app.use('/webhook', (req, res, next) => {
  const clientIP = req.ip || req.connection.remoteAddress;
  
  if (!allowedIPs.includes(clientIP)) {
    return res.status(403).send('Forbidden');
  }
  
  next();
});
```

### Data privacy

* **Webhook payloads contain customer PII** including emails and names
* **Ensure GDPR/CCPA compliance** in your data handling
* **Implement proper access controls** and audit logging
* **Consider data retention policies** for webhook data
