Help us shape the future of Go! Take the 2026 Go Developer Survey

webhook

package
v1.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package webhook defines the webhook domain entity and its store interface. This is for authsome's own webhook subscriptions. Actual delivery is delegated to Relay via the bridge.EventRelay interface.

Index

Constants

View Source
const (
	RelaySignatureHeader = "X-Relay-Signature"
	RelayTimestampHeader = "X-Relay-Timestamp"
)

RelaySignatureHeader and RelayTimestampHeader are the headers Relay, the delivery service behind authsome webhooks, sets on every delivery. The signature is the same HMAC-SHA256 over "<unix-seconds>.<body>" as X-Authsome-Signature, carried as "v1=<hex>" with the timestamp in its own header.

View Source
const DefaultSignatureTolerance = 5 * time.Minute

DefaultSignatureTolerance is the max clock skew allowed between sender and receiver before VerifySignature rejects with ErrTimestampSkew. Stripe uses 5 minutes; we match for familiarity.

View Source
const SignatureHeader = "X-Authsome-Signature"

SignatureHeader is the canonical HTTP header carrying the signature envelope on outgoing webhook deliveries.

Format (Stripe-style for operator familiarity):

X-Authsome-Signature: t=<unix-seconds>,v1=<hex(hmac-sha256)>

The HMAC is computed over `<unix-seconds>.<raw-body-bytes>` so the signature also binds the timestamp — an attacker can't replay an old signature with a fresh timestamp without knowing the secret.

View Source
const SignatureVersion = "v1"

SignatureVersion is the only currently-supported signature scheme. New schemes (e.g. ed25519) would land as v2 alongside v1 so receivers can roll forward without breaking existing senders.

Variables

View Source
var (
	ErrMalformedHeader   = errors.New("webhook: malformed signature header")
	ErrUnknownVersion    = errors.New("webhook: unknown signature version")
	ErrTimestampSkew     = errors.New("webhook: signature timestamp outside tolerance")
	ErrSignatureMismatch = errors.New("webhook: signature mismatch")
)

Errors returned by VerifySignature. Callers should match with errors.Is so the signaling stays stable across error wrapping.

Functions

func SignBody

func SignBody(secret []byte, ts time.Time, body []byte) string

SignBody computes the X-Authsome-Signature value for a given timestamp, raw request body, and shared secret. Senders compose this once per delivery and set the header on the outgoing request; receivers reproduce it under their own secret to verify.

The timestamp is included in the header in plaintext AND folded into the HMAC input — this is intentional. A receiver that only MAC'd the body would accept a replayed (body, signature) pair with a fresh timestamp; binding the timestamp closes that gap.

func SignRelay

func SignRelay(secret string, ts time.Time, body []byte) string

SignRelay produces the X-Relay-Signature value Relay would put on a delivery of body at ts: "v1=<hex hmac-sha256 over "<ts>.<body>">". The engine uses it for the test delivery it makes before registering a webhook, so the receiver sees exactly what Relay will send.

func StoredSecret

func StoredSecret(w *Webhook) string

StoredSecret is what a store writes to the secret column: nothing once the webhook delivers through a Relay endpoint (Relay holds the secret), and the plaintext only for a row that has not been adopted yet, so the legacy shape is preserved until adoption rewrites it.

func VerifyRelay

func VerifyRelay(body []byte, secret, timestamp, sig string, tolerance time.Duration) error

VerifyRelay checks a delivery from Relay: timestamp is the value of X-Relay-Timestamp, sig the value of X-Relay-Signature, secret the webhook's secret as shown when it was created or last rotated. A zero tolerance means DefaultSignatureTolerance.

func VerifySignature

func VerifySignature(secret, body []byte, header string, tolerance time.Duration) error

VerifySignature returns nil when header was produced by SignBody using the same secret over the same body within tolerance of now. Pass tolerance=0 to use DefaultSignatureTolerance.

Comparison is constant-time; failures are returned as wrapped sentinel errors so callers can distinguish "stale timestamp" (worth logging at debug, often a clock-drift symptom) from "MAC didn't match" (worth alerting — either a misconfigured secret or an attacker probing).

Types

type Store

type Store interface {
	CreateWebhook(ctx context.Context, w *Webhook) error
	GetWebhook(ctx context.Context, webhookID id.WebhookID) (*Webhook, error)
	UpdateWebhook(ctx context.Context, w *Webhook) error
	DeleteWebhook(ctx context.Context, webhookID id.WebhookID) error
	ListWebhooks(ctx context.Context, appID id.AppID) ([]*Webhook, error)
}

Store defines the persistence interface for webhook operations.

type Webhook

type Webhook struct {
	ID     id.WebhookID     `json:"id"`
	AppID  id.AppID         `json:"app_id"`
	EnvID  id.EnvironmentID `json:"env_id"`
	URL    string           `json:"url"`
	Events []string         `json:"events"`
	// Secret is the signing secret in plaintext. It is set on the way out
	// of CreateWebhook and RotateWebhookSecret, shown to the caller once,
	// and never stored: a row that has a RelayEndpointID reads back with an
	// empty Secret. Rows written before webhooks became Relay endpoints
	// still carry their plaintext until they are adopted, and read it back
	// so adoption can hand the same secret to Relay.
	Secret string `json:"-"`
	// SecretHash is store.HashToken of the current secret, so a caller can
	// check which secret they hold without the store holding it.
	SecretHash string `json:"-"`
	// RelayEndpointID names the Relay endpoint that delivers for this
	// webhook. Empty on a row written before webhooks became endpoints.
	RelayEndpointID string    `json:"relay_endpoint_id,omitempty"`
	Active          bool      `json:"active"`
	CreatedAt       time.Time `json:"created_at"`
	UpdatedAt       time.Time `json:"updated_at"`
}

Webhook represents a registered webhook endpoint.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL