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

# Validating sensor data

> Learn how to use a shared secret to validate the sensor data sent to a sensor endpoint.

# ✔️ Validate incoming data

Learn how to use a shared secret so we can validate the sensor data sent to a sensor endpoint.

If the data strategy selected for a sensor is **Endpoint**, Nexudus waits for data to be sent to that endpoint. To validate this data actually originates from a trusted source, a shared secret is used to sign the request.

A shared secret is automatically created for you when you register a new sensor. You can view and rotate this secret if needed within the details of each sensor.

## How signature validation works

To validate the signature of a request, we serialise the payload of the request and calculate a **HMACSHA256** signature using the shared secret. We will compare that signature with the value of the HTTP header `X-Nexudus-Hook-Signature` and only accept the request if there is an exact match.

<Note>
  Both the payload and shared secrets are converted to a byte array assuming **UTF-8** encoding. You may need to take this into consideration when
  computing the signature on your server in order to set the HTTP header.
</Note>

## Code examples

<CodeGroup>
  ```csharp C# theme={null}
  var SIGNATURE_HEADER_NAME = "X-Nexudus-Hook-Signature";
  var payload = Newtonsoft.Json.JsonConvert.SerializeObject(data);
  if (payload == null) return Unauthorized();

  var receivedHash = Request.Headers.GetValues(SIGNATURE_HEADER_NAME).FirstOrDefault();
  if (receivedHash == null) return Unauthorized();

  var keyByte = new UTF8Encoding().GetBytes(sharedSecret);
  var messageBytes = new UTF8Encoding().GetBytes(payload);

  var hmacsha256 = new HMACSHA256(keyByte);
  var hashBytes = hmacsha256.ComputeHash(messageBytes);
  var hash = ConvertByteToString(hashBytes);

  if (hash != receivedHash)
      return Unauthorized();
  ```

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

  SIGNATURE_HEADER_NAME = "X-Nexudus-Hook-Signature"

  def validate_sensor_data(request, shared_secret):
      payload = json.dumps(request.json, separators=(",", ":"))
      if payload is None:
          return False

      received_hash = request.headers.get(SIGNATURE_HEADER_NAME)
      if received_hash is None:
          return False

      computed_hash = hmac.new(
          shared_secret.encode("utf-8"),
          payload.encode("utf-8"),
          hashlib.sha256
      ).hexdigest()

      return hmac.compare_digest(computed_hash, received_hash)
  ```

  ```javascript Node.js theme={null}
  const crypto = require('crypto')

  const SIGNATURE_HEADER_NAME = 'X-Nexudus-Hook-Signature'

  function validateSensorData(req, sharedSecret) {
    const payload = JSON.stringify(req.body)
    if (!payload) return false

    const receivedHash = req.headers[SIGNATURE_HEADER_NAME.toLowerCase()]
    if (!receivedHash) return false

    const computedHash = crypto.createHmac('sha256', Buffer.from(sharedSecret, 'utf-8')).update(Buffer.from(payload, 'utf-8')).digest('hex')

    return crypto.timingSafeEqual(Buffer.from(computedHash), Buffer.from(receivedHash))
  }
  ```

  ```java Java theme={null}
  import javax.crypto.Mac;
  import javax.crypto.spec.SecretKeySpec;
  import java.nio.charset.StandardCharsets;

  public class SensorValidator {

      private static final String SIGNATURE_HEADER_NAME = "X-Nexudus-Hook-Signature";

      public static boolean validate(String payload, String receivedHash, String sharedSecret) {
          if (payload == null || receivedHash == null) return false;

          try {
              Mac hmac = Mac.getInstance("HmacSHA256");
              SecretKeySpec keySpec = new SecretKeySpec(
                  sharedSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"
              );
              hmac.init(keySpec);
              byte[] hashBytes = hmac.doFinal(payload.getBytes(StandardCharsets.UTF_8));

              StringBuilder sb = new StringBuilder();
              for (byte b : hashBytes) {
                  sb.append(String.format("%02x", b));
              }

              return sb.toString().equals(receivedHash);
          } catch (Exception e) {
              return false;
          }
      }
  }
  ```

  ```ruby Ruby theme={null}
  require "openssl"
  require "json"

  SIGNATURE_HEADER_NAME = "X-Nexudus-Hook-Signature"

  def validate_sensor_data(request, shared_secret)
    payload = request.body.read
    return false if payload.nil? || payload.empty?

    received_hash = request.env["HTTP_X_NEXUDUS_HOOK_SIGNATURE"]
    return false if received_hash.nil?

    computed_hash = OpenSSL::HMAC.hexdigest(
      "SHA256",
      shared_secret.encode("utf-8"),
      payload.encode("utf-8")
    )

    Rack::Utils.secure_compare(computed_hash, received_hash)
  end
  ```

  ```php PHP theme={null}
  <?php

  $SIGNATURE_HEADER_NAME = "X-Nexudus-Hook-Signature";

  function validateSensorData(string $payload, string $sharedSecret): bool
  {
      $receivedHash = $_SERVER["HTTP_X_NEXUDUS_HOOK_SIGNATURE"] ?? null;
      if ($receivedHash === null || $payload === "") {
          return false;
      }

      $computedHash = hash_hmac("sha256", $payload, $sharedSecret);

      return hash_equals($computedHash, $receivedHash);
  }
  ```
</CodeGroup>
