> ## Documentation Index
> Fetch the complete documentation index at: https://docs.scenext.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# Signature Verification

> Learn how to verify HMAC-SHA256 signatures for callback notifications

# Signature Verification

To ensure the security and integrity of callback notifications, Scenext uses the HMAC-SHA256 algorithm to sign all callback requests.

## Signature Algorithm

### Generation Process

1. Convert callback data to JSON string (sorted by key names)
2. Use your API key as the secret key
3. Generate signature using HMAC-SHA256 algorithm
4. Convert signature to hexadecimal string

### Python Implementation

```python theme={null}
import hmac
import hashlib
import json

def generate_signature(payload, api_key):
    """
    Generate HMAC-SHA256 signature
    :param payload: Data to be sent
    :param api_key: API key for signing
    :return: Hexadecimal signature
    """
    # Convert payload to JSON string
    if isinstance(payload, dict):
        payload_str = json.dumps(payload, sort_keys=True)
    else:
        payload_str = str(payload)
    
    # Generate signature using HMAC-SHA256
    signature = hmac.new(
        key=api_key.encode('utf-8'),
        msg=payload_str.encode('utf-8'),
        digestmod=hashlib.sha256
    ).hexdigest()
    
    return signature

def verify_signature(payload, signature, api_key):
    """
    Verify HMAC-SHA256 signature
    """
    expected_signature = generate_signature(payload, api_key)
    return hmac.compare_digest(expected_signature, signature)
```

### JavaScript Implementation

```javascript theme={null}
const crypto = require('crypto');

function generateSignature(payload, apiKey) {
    // Convert payload to JSON string (sorted by keys)
    const payloadStr = JSON.stringify(payload, Object.keys(payload).sort());
    
    // Generate signature
    const signature = crypto
        .createHmac('sha256', apiKey)
        .update(payloadStr, 'utf8')
        .digest('hex');
    
    return signature;
}

function verifySignature(payload, signature, apiKey) {
    const expectedSignature = generateSignature(payload, apiKey);
    
    return crypto.timingSafeEqual(
        Buffer.from(expectedSignature, 'hex'),
        Buffer.from(signature, 'hex')
    );
}
```

### PHP Implementation

```php theme={null}
<?php
function generateSignature($payload, $apiKey) {
    // Convert payload to JSON string (sorted by keys)
    if (is_array($payload)) {
        ksort($payload);
        $payloadStr = json_encode($payload);
    } else {
        $payloadStr = (string)$payload;
    }
    
    // Generate signature
    return hash_hmac('sha256', $payloadStr, $apiKey);
}

function verifySignature($payload, $signature, $apiKey) {
    $expectedSignature = generateSignature($payload, $apiKey);
    return hash_equals($expectedSignature, $signature);
}
?>
```

## Complete Verification Examples

### Flask (Python) Server

```python theme={null}
from flask import Flask, request, jsonify
import hmac
import hashlib
import json

app = Flask(__name__)

API_KEY = "your_api_key_here"  # Your API key

def verify_signature(payload, signature):
    """Verify signature"""
    payload_str = json.dumps(payload, sort_keys=True)
    expected_signature = hmac.new(
        key=API_KEY.encode('utf-8'),
        msg=payload_str.encode('utf-8'),
        digestmod=hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(expected_signature, signature)

@app.route('/webhook', methods=['POST'])
def webhook():
    # Get signature
    signature = request.headers.get('X-Signature')
    if not signature:
        return jsonify({'error': 'Missing signature'}), 400
    
    # Get request data
    try:
        data = request.get_json()
    except Exception:
        return jsonify({'error': 'Invalid JSON'}), 400
    
    # Verify signature
    if not verify_signature(data, signature):
        return jsonify({'error': 'Invalid signature'}), 401
    
    # Process callback data
    task_id = data.get('task_id')
    status = data.get('status')
    
    print(f"Received webhook for task {task_id} with status {status}")
    
    # Process based on status
    if status == 'COMPLETED':
        # Task completion handling
        result = data.get('result', {})
        video_url = result.get('video_url')
        print(f"Video completed: {video_url}")
        
    elif status == 'FAILED':
        # Task failure handling
        result = data.get('result', {})
        error = result.get('error')
        print(f"Video generation failed: {error}")
    
    return jsonify({'message': 'OK'}), 200

if __name__ == '__main__':
    app.run(debug=True)
```

### Express (Node.js) Server

```javascript theme={null}
const express = require('express');
const crypto = require('crypto');

const app = express();
const API_KEY = 'your_api_key_here';  // Your API key

app.use(express.json());

function verifySignature(payload, signature) {
    const payloadStr = JSON.stringify(payload, Object.keys(payload).sort());
    const expectedSignature = crypto
        .createHmac('sha256', API_KEY)
        .update(payloadStr, 'utf8')
        .digest('hex');
    
    return crypto.timingSafeEqual(
        Buffer.from(expectedSignature, 'hex'),
        Buffer.from(signature, 'hex')
    );
}

app.post('/webhook', (req, res) => {
    const signature = req.headers['x-signature'];
    if (!signature) {
        return res.status(400).json({ error: 'Missing signature' });
    }
    
    const data = req.body;
    
    if (!verifySignature(data, signature)) {
        return res.status(401).json({ error: 'Invalid signature' });
    }
    
    // Process callback data
    const { task_id, status, result } = data;
    
    console.log(`Received webhook for task ${task_id} with status ${status}`);
    
    if (status === 'COMPLETED') {
        console.log(`Video completed: ${result.video_url}`);
    } else if (status === 'FAILED') {
        console.log(`Video generation failed: ${result.error}`);
    }
    
    res.status(200).json({ message: 'OK' });
});

app.listen(3000, () => {
    console.log('Webhook server listening on port 3000');
});
```

## Security Best Practices

<Card title="Important Tips" icon="shield-alt">
  <CardBody>
    <ul>
      <li>**Keep API keys secure**: Do not expose API keys in client-side code</li>
      <li>**Use HTTPS**: Ensure callback URLs use HTTPS protocol</li>
      <li>**Verify timestamps**: Check timestamps to prevent replay attacks</li>
      <li>**Implement idempotency**: Ensure duplicate callbacks don't cause issues</li>
      <li>**Log everything**: Record all callback requests for debugging and auditing</li>
    </ul>
  </CardBody>
</Card>

## FAQ

<AccordionGroup>
  <Accordion title="What to do if signature verification fails?">
    1. Check if the API key is correct
    2. Ensure JSON serialization sorts keys by name
    3. Verify that request data hasn't been modified
    4. Check that character encoding is UTF-8
  </Accordion>

  <Accordion title="Can I skip signature verification?">
    We strongly recommend not skipping signature verification. This would expose your system to security risks, including but not limited to: forged callback requests, data tampering, etc.
  </Accordion>

  <Accordion title="Is timestamp verification required?">
    Timestamp verification is not required but recommended. You can check if the timestamp is within a reasonable range (e.g., within 5 minutes) to prevent replay attacks.
  </Accordion>
</AccordionGroup>
