AshHooks.Legacy (AshHooks v1.0.1)

Copy Markdown View Source

The incumbent webhook envelope, reproduced byte-identically for the :dual migration mode (ADR-0002), with verify/5 as the in-package oracle proving that identity.

Envelope (shape extracted first-hand from the adopting platform's Webhooks.Signing + its deliver worker — read-only reference):

  • MAC — HMAC-SHA256 over "<unix_ts>.<body>", lowercase hex.
  • sign/3"v1=<hex>"; rotation appends ",v1prev=<hex>" under the separately imported previous secret.
  • header — x-webhook-signature: t=<unix_ts>,<signature>.

This module exists for migration and is scheduled to shrink away as subscriptions cut over to :standard.

Summary

Functions

Assembles the header value: t=<unix_ts>,<signature>.

The legacy header set: x-webhook-signature under secret (and optional previous).

HMAC-SHA256 over <unix_ts>.<body>, lowercase hex, v1=-prefixed.

Current and previous signatures comma-joined (zero-downtime rotation).

Verifies a legacy header value: parses t=/v1=, checks the replay window, compares hex digests in constant time.

Functions

header_value(signature, unix_ts)

@spec header_value(String.t(), integer()) :: String.t()

Assembles the header value: t=<unix_ts>,<signature>.

headers(secret, previous \\ nil, body, unix_ts)

@spec headers(binary(), binary() | nil, binary(), integer()) :: %{
  required(String.t()) => String.t()
}

The legacy header set: x-webhook-signature under secret (and optional previous).

sign(secret, body, unix_ts)

@spec sign(binary(), binary(), integer()) :: String.t()

HMAC-SHA256 over <unix_ts>.<body>, lowercase hex, v1=-prefixed.

sign_with_previous(current_secret, previous_secret, body, unix_ts)

@spec sign_with_previous(binary(), binary(), binary(), integer()) :: String.t()

Current and previous signatures comma-joined (zero-downtime rotation).

verify(secret, body, header, now_ts, window \\ 300)

@spec verify(binary(), binary(), binary(), integer(), integer()) ::
  {:ok, %{unix_ts: integer()}} | {:error, :invalid_signature | :stale_timestamp}

Verifies a legacy header value: parses t=/v1=, checks the replay window, compares hex digests in constant time.

window defaults to 300 seconds, symmetric around now_ts.