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

# Authentication

> Learn how to verify that incoming requests to your integration are authentically from Wavy Node using HMAC-SHA256.

Wavy Node uses HMAC-SHA256 to sign every request it sends to your integration. This guarantees that requests are legitimate and haven't been tampered with in transit.

Think of it as a seal of authenticity: only someone with the shared `SECRET` can generate a valid signature.

## How it works

```mermaid theme={null}
sequenceDiagram
    participant WN as Wavy Node
    participant YS as Your Server

    WN->>WN: 1. Build canonical message
    WN->>WN: 2. Sign with HMAC-SHA256 using SECRET
    WN->>YS: 3. Send request + headers (x-wavynode-hmac, x-wavynode-timestamp)
    YS->>YS: 4. Rebuild canonical message
    YS->>YS: 5. Recalculate signature with SECRET
    alt Signatures match
        YS-->>WN: 200 OK ✅
    else Signatures don't match
        YS-->>WN: 401 Unauthorized ❌
    end
```

### Step by step

1. Wavy Node builds a canonical message from: HTTP method, path, body, and timestamp
2. Signs that message with HMAC-SHA256 using the shared SECRET
3. Sends the request with two extra headers:
   * `x-wavynode-hmac` — the generated signature
   * `x-wavynode-timestamp` — the timestamp in milliseconds
4. Your server receives the request
5. Recalculates the signature using the same SECRET and request data
6. If signatures match → the request is legitimate (200). If not → reject it (401)

## The shared SECRET

The SECRET is a 32-character hex string that both sides know:

```
615d9b7ea991acfa33f823c374c3a062
```

It must be configured in two places:

| Location             | How to configure                                |
| -------------------- | ----------------------------------------------- |
| Wavy Node dashboard  | **Settings → Integración → Integration Secret** |
| Your server (`.env`) | `SECRET=615d9b7ea991acfa33f823c374c3a062`       |

Both values must be exactly the same. If they differ, signatures won't match and all requests will fail with 401.

### Generate a new SECRET

```bash theme={null}
openssl rand -hex 16
```

## Authentication headers

Every request from Wavy Node includes these headers:

| Header                 | Type   | Description                                                   |
| ---------------------- | ------ | ------------------------------------------------------------- |
| `x-wavynode-hmac`      | string | Base64 encoded HMAC-SHA256 signature of the canonical message |
| `x-wavynode-timestamp` | string | Timestamp in milliseconds (epoch)                             |

## Canonical message

The signature is not calculated on the raw request, but on a standardized canonical message. This ensures both sides build exactly the same string before signing.

The canonical message is formed by concatenating these values separated by `::`:

* The uppercase HTTP method (`GET`, `POST`, etc.)
* The lowercase request path (e.g., `/webhook`)
* The stringified request body with keys sorted alphabetically, or `{}` if no body
* The timestamp from the `x-wavynode-timestamp` header

```
GET::/users/123::{}::1757050233763
```

## Using `@wavynode/utils`

The easiest way to verify requests is with the `validateSignature` function:

```typescript theme={null}
import { validateSignature } from '@wavynode/utils';

const isValid = validateSignature({
    method: 'POST',
    path: '/webhook',
    body: req.body,
    timestamp: parseInt(req.headers['x-wavynode-timestamp']),
    secret: process.env.SECRET,
    timeTolerance: 300000,
    signature: req.headers['x-wavynode-hmac']
});

if (!isValid) {
    // The request is not from Wavy Node
    // or has been tampered with.
    // Reject the request.
}
```

<Accordion title="validateSignature parameters">
  | Parameter       | Type   | Description                                                                      |
  | --------------- | ------ | -------------------------------------------------------------------------------- |
  | `method`        | string | The HTTP method of the request                                                   |
  | `path`          | string | The path of the request                                                          |
  | `body`          | object | The request body                                                                 |
  | `timestamp`     | number | The timestamp from the `x-wavynode-timestamp` header                             |
  | `secret`        | string | Your integration's secret from **Settings → Integración** in the dashboard       |
  | `timeTolerance` | number | Time tolerance in milliseconds to prevent replay attacks (recommended: `300000`) |
  | `signature`     | string | The signature from the `x-wavynode-hmac` header                                  |
</Accordion>

You can also build the canonical message manually with `formCanonicalMessage`:

```typescript theme={null}
import { formCanonicalMessage } from "@wavynode/utils";

const canonicalMessage = formCanonicalMessage({
    method: "GET",
    path: "/users/user-123",
    body: {},
    timestamp: 1712764800000
});
```

## Manual authentication

If you are not using the `@wavynode/utils` package, you can implement the authentication logic yourself.

<Steps>
  <Step title="Create the canonical string">
    Concatenate the values separated by `::` as described above:

    ```
    GET::/users/123::{}::1757050233763
    ```
  </Step>

  <Step title="Create the HMAC signature">
    Create a `sha256` HMAC of the canonical string using your integration's secret as the key. Base64 encode the result.

    ```javascript theme={null}
    import crypto from 'node:crypto';

    const createHmacSignature = (message, secret) => {
        const hmac = crypto.createHmac('sha256', secret);
        hmac.update(message);
        return hmac.digest('base64');
    };
    ```
  </Step>

  <Step title="Compare the signatures">
    Compare the signature you created with the one from the `x-wavynode-hmac` header. If they match, the request is authentic.
  </Step>
</Steps>

## Why is HMAC necessary?

Without HMAC authentication, anyone who knows your integration URL could:

* Send fake webhooks simulating transactions that never happened
* Query user data without authorization
* Inject false notifications into your system

HMAC guarantees that only Wavy Node can communicate with your integration server.
