Magpie.Webhook (Magpie v0.8.0)

Copy Markdown View Source

Framework-independent Dropbox webhook verification and notification decoding.

Notifications contain account IDs, not file changes. Enqueue those IDs and use each account's client and saved cursor with Magpie.Storage.continue_list/3. Respond promptly; serialize workers per account and apply pages idempotently. This module provides no persistence, queue, deduplication or replay protection. See the incremental and webhook guide.

Summary

Functions

Returns {:ok, %{status: 200, headers: headers, body: challenge}} for a non-empty binary challenge, otherwise {:error, :invalid_challenge}. Headers are content-type: text/plain and x-content-type-options: nosniff. Echo the decoded query value as plain text without HTML interpolation.

Validates the signature before decoding JSON; returns {:ok, [account_id]}, {:ok, :ignored}, {:error, :invalid_signature} or {:error, :invalid_payload}.

Checks the hexadecimal X-Dropbox-Signature using HMAC-SHA256 over original binary body bytes and the app secret. Compares equal-sized digests in constant time. Returns false for absent, malformed or invalid signatures/arguments. Never encode parsed JSON to reconstruct the signed body.

Functions

challenge(challenge)

@spec challenge(term()) :: {:ok, map()} | {:error, :invalid_challenge}

Returns {:ok, %{status: 200, headers: headers, body: challenge}} for a non-empty binary challenge, otherwise {:error, :invalid_challenge}. Headers are content-type: text/plain and x-content-type-options: nosniff. Echo the decoded query value as plain text without HTML interpolation.

notification(body, signature, secret)

@spec notification(binary(), term(), binary()) ::
  {:ok, [binary()] | :ignored} | {:error, :invalid_signature | :invalid_payload}

Validates the signature before decoding JSON; returns {:ok, [account_id]}, {:ok, :ignored}, {:error, :invalid_signature} or {:error, :invalid_payload}.

A signed JSON object without list_folder is an unsupported notification: return {:ok, :ignored} so the endpoint can acknowledge it without enqueueing accounts. Consumers may observe this result to detect formats they don't handle. This acknowledges receipt, not support for Business/team notifications.

When list_folder is present, accounts must be a list of non-empty binary IDs. Malformed JSON, non-object JSON and malformed recognized notifications return :invalid_payload. Empty lists are valid; order and repeated IDs are preserved. Extra fields, including legacy delta, are ignored.

valid_signature?(body, signature, secret)

@spec valid_signature?(binary(), term(), binary()) :: boolean()

Checks the hexadecimal X-Dropbox-Signature using HMAC-SHA256 over original binary body bytes and the app secret. Compares equal-sized digests in constant time. Returns false for absent, malformed or invalid signatures/arguments. Never encode parsed JSON to reconstruct the signed body.