Harden a Microsoft Graph Webhook Listener

Microsoft Graph webhooks notify Hermes that an event occurred; they should not be treated as trusted instructions by default. The listener's primary authentication check is the subscription's clientState, supported by HTTPS, response hygiene, and optional network controls.

Official documentation: https://hermes-agent.nousresearch.com/docs/user-guide/messaging/msgraph-webhook

Treat notification and content retrieval separately

A Graph notification can identify a changed resource, but the downstream pipeline may still need to retrieve and authorize that resource through Microsoft Graph. Keep the webhook handler small: validate the subscription challenge or notification, reject invalid state, enqueue bounded work, and respond within the platform's timing expectations.

Generate a strong clientState per deployment or subscription family and store it as a secret. Compare it exactly on every notification. Do not place it in query strings, logs, screenshots, or public sample configuration.

Hardening checklist

  • Define the exact Graph resources and change types required.
  • Configure the listener on an HTTPS endpoint.
  • Generate and protect a strong clientState value.
  • Validate subscription challenges without exposing internal data.
  • Reject notifications with missing or mismatched state.
  • Return minimal responses and avoid reflecting payloads.
  • Add source-IP controls only as defense in depth, not the primary check.
  • Record subscription expiration and renewal ownership.
  • Give downstream Graph credentials least privilege.

Control the downstream job

Deduplicate notifications before expensive agent work. Fetch the authoritative resource after authentication rather than trusting optional data embedded in the notification. Apply size limits, content-type checks, and tenant or resource allowlists before transcript, file, or meeting processing begins.

Subscription renewal is an operational dependency. Alert before expiration and verify the replacement subscription points to the same reviewed endpoint and state policy.

Common pitfalls

  • Treating source IP as the only authentication factor.
  • Logging clientState while debugging failed validation.
  • Doing long model work inside the webhook response path.
  • Trusting notification payload fields without fetching the authoritative resource.
  • Creating duplicate downstream jobs for repeated notifications.
  • Forgetting that Graph subscriptions expire and require renewal.

Verification steps

  1. Complete the Graph validation handshake over the live HTTPS endpoint.
  2. Send a controlled notification with the correct clientState and confirm acceptance.
  3. Repeat with missing and incorrect state and confirm rejection.
  4. Replay the valid notification and verify deduplication prevents duplicate work.
  5. Confirm the downstream token can read only the intended resource scope.
  6. Check the subscription expiration and rehearse renewal before the deadline.
  7. Review access logs for reflected payloads, secret leakage, long response times, and unexpected tenants.