Skip to content

MT-22022: Add webhook signature verification helper#67

Open
Rabsztok wants to merge 1 commit into
mainfrom
MT-22022-webhook-signature-verification
Open

MT-22022: Add webhook signature verification helper#67
Rabsztok wants to merge 1 commit into
mainfrom
MT-22022-webhook-signature-verification

Conversation

@Rabsztok
Copy link
Copy Markdown

@Rabsztok Rabsztok commented May 20, 2026

Motivation

Expose a helper so Python users don't have to re-implement Mailtrap's HMAC-SHA256 webhook signature check on every receiver.

Changes

  • mailtrap.webhooks.verify_signature(payload, signature, signing_secret)True/False. Re-exported as mailtrap.verify_signature for mt.verify_signature(...) usage. HMAC-SHA256 over the raw body, constant-time compare via hmac.compare_digest. Accepts payload as str (UTF-8) or bytes. Returns False (never raises) on empty / wrong-length / non-hex / wrong-type inputs.
  • tests/unit/test_webhook_signature.py pins the cross-SDK fixture (payload + signing_secret + expected digest) shared verbatim across all six official Mailtrap SDKs to guarantee byte-for-byte parity.
  • examples/webhooks/verify_signature.py — runnable usage snippet.
  • README — new "Verifying webhook signatures" subsection.

How to test

  • pytest tests/unit/test_webhook_signature.py -v — 10 examples, 0 failures
  • pytest — full suite, 461 passed
  • pre-commit run --all-files — all checks pass
  • mypy ./mailtrap — Success: no issues found in 63 source files
  • python examples/webhooks/verify_signature.py — exits 0

Companion PRs

Coordinated rollout across all six official SDKs (same algorithm, same shared fixture):

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

📝 Walkthrough

Walkthrough

This PR adds webhook signature verification to the Mailtrap Python SDK using HMAC-SHA256 constant-time comparison. The implementation includes core validation logic, module exports, comprehensive tests with shared fixture constants, and user-facing documentation with examples.

Changes

Webhook signature verification

Layer / File(s) Summary
Core verification implementation
mailtrap/webhooks.py
Introduces verify_signature(payload, signature, signing_secret) with input validation, UTF-8 payload encoding, HMAC-SHA256 digest computation, and constant-time comparison via hmac.compare_digest; returns False defensively on invalid inputs.
SDK module export
mailtrap/__init__.py
Re-exports verify_signature from .webhooks at the package top level.
Test suite and fixtures
tests/unit/test_webhook_signature.py
Comprehensive unit tests with cross-SDK fixture constants, covering correct signature verification, rejection of tampering/wrong secret/malformed signatures, edge cases (empty inputs, non-hex, too-short), regression check against hardcoded HMAC-SHA256 digest, and bytes payload support.
Documentation and examples
README.md, examples/webhooks/verify_signature.py
README explains signature verification using Mailtrap-Signature header and raw request body bytes with documented False return behavior; example script demonstrates correct verification and failure cases.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 Webhooks now sign with secrets so fine,
HMAC-SHA256 keeps payloads in line,
Verify with constants, defensively sound,
Your signatures safe and forever found! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding a webhook signature verification helper to the SDK.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering motivation, detailed changes with implementation specifics, testing instructions, and links to companion PRs.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch MT-22022-webhook-signature-verification

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Add `mailtrap.webhooks.verify_signature` (re-exported as `mt.verify_signature`)
to verify Mailtrap webhook signatures using HMAC-SHA256 over the raw request
body with constant-time hex comparison via `hmac.compare_digest`.

Returns False (no raise) for missing/empty/malformed/wrong-length signatures
so a single guard at the request handler covers every bad-input case. Accepts
both `str` and `bytes` payloads; signed input must be the raw body bytes (the
helper docstring and example warn against re-serializing parsed JSON).

Includes the shared cross-SDK test fixture (payload + secret + expected
signature) that all six Mailtrap SDKs use to stay byte-for-byte compatible,
plus a Flask receiver example and README subsection.

See https://railsware.atlassian.net/browse/MT-22022
@Rabsztok Rabsztok force-pushed the MT-22022-webhook-signature-verification branch from 35b2788 to 129bc81 Compare May 20, 2026 10:58
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
README.md (1)

249-274: 💤 Low value

Clear and accurate webhook signature documentation.

The explanation of HMAC-SHA256 verification is well-written, and the important warning about using raw request body bytes (not re-serialized JSON) is prominently placed.

Consider adding a brief note about framework-specific approaches for obtaining the raw request body, e.g.:

**Framework examples:**
- Flask: `raw_body = request.get_data()`
- FastAPI: `raw_body = await request.body()`
- Django: `raw_body = request.body`

This would help users who are unfamiliar with accessing raw request bodies in their chosen framework, though the current framework-agnostic approach is also valid.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 249 - 274, Add a short framework-specific note under
the "Verifying webhook signatures" section to show how to obtain the raw request
body for common Python frameworks (e.g., Flask, FastAPI, Django) so users can
correctly pass raw_body to mt.verify_signature; reference the Mailtrap-Signature
header and the signing_secret usage, and keep the note concise and clearly
labeled (e.g., "Framework examples") to avoid changing the existing verification
guidance.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@README.md`:
- Around line 249-274: Add a short framework-specific note under the "Verifying
webhook signatures" section to show how to obtain the raw request body for
common Python frameworks (e.g., Flask, FastAPI, Django) so users can correctly
pass raw_body to mt.verify_signature; reference the Mailtrap-Signature header
and the signing_secret usage, and keep the note concise and clearly labeled
(e.g., "Framework examples") to avoid changing the existing verification
guidance.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 99d05986-8255-4a01-bbc5-c16791711838

📥 Commits

Reviewing files that changed from the base of the PR and between 294d573 and 129bc81.

📒 Files selected for processing (5)
  • README.md
  • examples/webhooks/verify_signature.py
  • mailtrap/__init__.py
  • mailtrap/webhooks.py
  • tests/unit/test_webhook_signature.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant