Skip to content

Fail-closed webhook authentication

ADR-003: Fail-closed webhook authentication

Status: Accepted Date: 2026-04-14 Deciders: Mishaal Murawala (enforced during security audit)

Context

The Cal.com webhook handler at /webhooks/calcom receives booking lifecycle events (created, rescheduled, cancelled) and triggers Slack notifications. The initial implementation accepted unauthenticated requests if CALCOM_WEBHOOK_SECRET was not configured — a fail-open design.

A security audit identified this as CRITICAL: any attacker reaching the endpoint could forge booking events, triggering fake Slack notifications and potentially poisoning scheduling state.

Decision

We will fail closed — if CALCOM_WEBHOOK_SECRET is not set, the webhook handler returns HTTP 500 with CONFIG_MISSING error. Additionally, HMAC verification uses crypto.subtle.verify() for timing-safe comparison instead of string equality.

Consequences

Positive

  • No unauthenticated webhook processing is possible, even during initial setup
  • Timing-safe HMAC comparison prevents side-channel attacks
  • Explicit error message tells the operator exactly what’s missing

Negative

  • Cal.com webhooks won’t work until the secret is configured — requires manual setup step during onboarding
  • Adds a dependency on CALCOM_WEBHOOK_SECRET wrangler secret for any tenant using Cal.com

Risks

  • If the secret is rotated in Cal.com but not in the Worker, all webhooks will be rejected until the Worker secret is updated. Mitigated by the explicit error logging.

Alternatives Considered

Fail-open with logging (accept but log warning)

  • Rejected because: “log and accept” means the system is vulnerable until someone reads the logs. Security boundaries must be enforced, not observed.

IP allowlisting instead of HMAC

  • Rejected because: Cal.com’s outbound IPs are not guaranteed stable, and IP-based auth is weaker than cryptographic verification.