> ## 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.

# Validate the signature of your Webhook Endpoint

Fintoc signs every event sent to your webhook endpoints with the `Fintoc-Signature` header. This header allows you to verify that every event received was in fact sent by Fintoc and not by a third party.

Fintoc generates a secret that is shown only once at the moment of registration of a Webhook Endpoint. This secret is necessary to validate the Fintoc signature of every event.

# Verify that the events are being sent by Fintoc

Each event sent by Fintoc includes the `Fintoc-Signature` header. The header structure is the same for every event. It contains a *timestamp* denoted by the `t` key and a signature denote by the `v1` key. Here is an example of a `Fintoc-Signature` header:

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

Signatures sent by Fintoc are just HMAC codes generated when signing the raw body of the request with the SHA-256 hash function at the moment indicated by the timestamp and using the Webhook Endpoint secret.

Let's begin validating the event's signature!

# Validate the signature using our SDK

If you’re using Python or Node, the easiest way to verify webhook signatures is with our [Python SDK](https://github.com/fintoc-com/fintoc-python) or [Node SDK](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
    }
  }
});
 
```

For a complete working example, check the [full code here](https://github.com/fintoc-com/fintoc-python/blob/master/examples/webhook.py).

# Validating the signature using your code

If you want to write your own implementation or use a different programming language, follow these steps:

## Extracting the timestamp and the header signature

Split the `Fintoc-Signature` header into an array by the `,` character. Then, split each element of the generated array by the `=` character to obtain a key-value pair. Finally, get the corresponding values for each key.

```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(',')]
```

## Re-building the signed message

The message that Fintoc signed before sending the webhook corresponds to the timestamp concatenated to a `.` character and the the raw body of the request. Let's re-build that same message using the timestamp that we just untangled from the `Fintoc-Signature` header and the raw body of the request.

```python theme={null}
import json

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

Make sure to **use the*raw* request body** directly from the request. Different libraries can represent the parsed JSON differently. An example of the value that `message` should have would be the following:

```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"}'
```

## Generating the signature

Now that you have the message that needs to be signed, let's do just that, using the SHA-256 hash function and the secret generated when creating the webhook endpoint.

```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()
```

## Comparing the signatures

Finally, let's compare the signature that we untangled from the `Fintoc-Signature` header to the one that we just obtained following the guide's steps. If both signatures match, then you can assume that the event was in fact sent by Fintoc.

```python theme={null}
import hmac

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

# Preventing a replay attack

To avoid a replay attack, we recommend that you define a tolerance range for event delays. When receiving and obtaining an event's timestamp, verify that it falls within your tolerance range. If that is not the case, discard the event.
