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

# Valida la firma de tu endpoint de Webhook

Fintoc firma cada evento enviado a tus endpoints de webhook con el header `Fintoc-Signature`. Este header te permite verificar que cada evento recibido fue, de hecho, enviado por Fintoc y no por un tercero.

Fintoc genera un secreto que se muestra solo una vez al momento de registrar un endpoint de Webhook. Este secreto es necesario para validar la firma de Fintoc de cada evento.

# Verifica que los eventos sean enviados por Fintoc

Cada evento enviado por Fintoc incluye el header `Fintoc-Signature`. La estructura del header es la misma para cada evento. Contiene un *timestamp* denotado por la clave `t` y una firma denotada por la clave `v1`. Aquí hay un ejemplo de un header `Fintoc-Signature`:

```text theme={null}
t=1620870928,v1=4df951e02db34a3f333bccad26d207993e9b14d78ac77cec026091991f567f6d
```

Las firmas enviadas por Fintoc son simplemente códigos HMAC generados al firmar el body sin procesar de la solicitud con la función hash SHA-256 en el momento indicado por el timestamp y usando el secreto del endpoint de Webhook.

¡Empecemos a validar la firma del evento!

# Validar la firma usando nuestro SDK

Si estás usando Python o Node, la forma más fácil de verificar las firmas de los webhooks es con nuestro [SDK de Python](https://github.com/fintoc-com/fintoc-python) o [SDK de Node](https://github.com/fintoc-com/fintoc-node).

```python Python theme={null}
from fintoc.webhook import WebhookSignature
from fintoc.errors import WebhookSignatureError

WEBHOOK_SECRET = os.getenv('FINTOC_WEBHOOK_SECRET')

# Rest of your code 
# ...

@app.route('/webhook', methods=['POST'])
def handle_webhook():
  # Get the signature header
  signature = request.headers.get('Fintoc-Signature')

  # Get the raw request payload
  payload = request.get_data().decode('utf-8')

  # Verify the webhook signature
  try:
    WebhookSignature.verify_header(
      payload=payload,
      header=signature,
      secret=WEBHOOK_SECRET
    )

  except WebhookSignatureError as e:
    print('Invalid signature!')
    return str(e), 400
```

```node Node theme={null}
const { WebhookSignature, WebhookSignatureError } = require('fintoc');

// Find your endpoint's secret in your webhook settings in the Fintoc Dashboard
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

// Rest of your code here
// ...

app.post('/webhook', (req, res) => {
  const payload = req.body;
  
  // Get the signature header
  const signature = req.headers['fintoc-signature'];

  try {
    // Verify the webhook signature
    WebhookSignature.verifyHeader(
      payload,
      signature,
      WEBHOOK_SECRET
    );

    // If verification passes, process the webhook
    const event = JSON.parse(payload.toString());
    
    // Rest of your code...


  } catch (error) {
    if (error instanceof WebhookSignatureError) {
      console.error('Webhook signature verification failed');
      res.status(400).json({
        error: 'Invalid signature',
        message: error.message
      });
    } else {
      // Other error
    }
  }
});
 
```

Para un ejemplo completo y funcional, revisa el [código completo aquí](https://github.com/fintoc-com/fintoc-python/blob/master/examples/webhook.py).

# Validar la firma usando tu propio código

Si quieres escribir tu propia implementación o usar un lenguaje de programación distinto, sigue estos pasos:

## Extraer el timestamp y la firma del header

Divide el header `Fintoc-Signature` en un arreglo usando el carácter `,`. Luego, divide cada elemento del arreglo generado usando el carácter `=` para obtener un par clave-valor. Finalmente, obtén los valores correspondientes a cada clave.

```python theme={null}
# using flask request
header_value = request.headers.get('Fintoc-Signature')
timestamp, event_signature = [x.split('=')[1] for x in header_value.split(',')]
```

## Reconstruir el mensaje firmado

El mensaje que Fintoc firmó antes de enviar el webhook corresponde al timestamp concatenado a un carácter `.` y al body sin procesar de la solicitud. Reconstruyamos ese mismo mensaje usando el timestamp que acabamos de extraer del header `Fintoc-Signature` y el body sin procesar de la solicitud.

```python theme={null}
import json

# using flask request
message = f"{timestamp}.{request.get_data().decode('utf-8')}"
```

Asegúrate de **usar el body de la solicitud *sin procesar*** directamente desde la solicitud. Distintas librerías pueden representar el JSON parseado de forma diferente. Un ejemplo del valor que `message` debería tener sería el siguiente:

```python theme={null}
'1626102791.{"id":"evt_DyzYBwdC07ao5MqG","type":"link.credentials_changed","mode":"test","created_at":"2021-07-12T15:11:09.875Z","data":{"id":"link_00000000","mode":"test","active":true,"object":"link","status":"active","accounts":null,"username":"416148503","holder_id":"416148503","created_at":"2021-06-24T00:00:00.000Z","link_token":null,"holder_type":"individual","institution":{"id":"cl_banco_bbva","name":"Banco BBVA","country":"cl"}},"object":"event"}'
```

## Generar la firma

Ahora que tienes el mensaje que necesita ser firmado, hagamos justamente eso, usando la función hash SHA-256 y el secreto generado al crear el endpoint de webhook.

```python theme={null}
import hmac
from hashlib import sha256

encoded_secret = YOUR_WEBHOOK_SECRET.encode('utf-8')
encoded_message = message.encode('utf-8')
hmac_object = hmac.new(encoded_secret, msg=encoded_message, digestmod=sha256)
signature = hmac_object.hexdigest()
```

## Comparar las firmas

Finalmente, comparemos la firma que extrajimos del header `Fintoc-Signature` con la que recién obtuvimos siguiendo los pasos de la guía. Si ambas firmas coinciden, entonces puedes asumir que el evento fue, de hecho, enviado por Fintoc.

```python theme={null}
import hmac

valid_signature = hmac.compare_digest(signature, event_signature)
```

# Prevenir un ataque de replay

Para evitar un ataque de replay, te recomendamos definir un rango de tolerancia para los retrasos de los eventos. Al recibir y obtener el timestamp de un evento, verifica que esté dentro de tu rango de tolerancia. Si no es así, descarta el evento.
