Skip to content

feat(helpers): verify webhook signatures without a Standard Webhooks library - #4

Draft
antonwhop wants to merge 2 commits into
anton/release-pipelinefrom
anton/webhook-signature-verification
Draft

feat(helpers): verify webhook signatures without a Standard Webhooks library#4
antonwhop wants to merge 2 commits into
anton/release-pipelinefrom
anton/webhook-signature-verification

Conversation

@antonwhop

Copy link
Copy Markdown
Collaborator

Why

client.webhooks.unwrap shipped in the Stainless SDK and every Fern SDK lost it — Fern generates from OpenAPI paths and unwrap was never a path. Ruby, Python and TypeScript have it back as hand-written helpers; Java did not, so a Java consumer had no way to tell a genuine Whop delivery from a forged one.

What changed?

  • com.whop.api.helpers.WebhookVerifier.unwrap(payload, headers, key) verifies the webhook-id / webhook-timestamp / webhook-signature triple and returns the parsed body as a JsonNode. Standalone, so no generated file is patched, and it adds no dependency: javax.crypto.Mac, java.util.Base64 and java.security.MessageDigest are JDK, so it HMACs the raw ws_… secret directly instead of fighting the Standard Webhooks libraries' base64-decoded key — the bug that made all three sibling helpers reject real deliveries until each base64-encoded the whole secret to cancel it out.
  • 22 unit tests, every fixture signed the way WebhooksManager::SignWebhook signs and the fixture signer itself pinned to a vector computed in Python. Covers a valid signature, tampered body, wrong secret, each of the three headers missing, stale and future timestamps, a multi-entry header whose valid v1 entry is not first, mixed header casing, and byte-exactness (a re-serialised body is refused).
  • .fernignore was a single comment line and protected nothing. It now keeps src/{main,test}/java/com/whop/api/helpers, and ci.yml fails if either the files or the entries go missing. build.gradle stays Fern-owned, as ci: release pipeline for the Java SDK, publishing to GitHub Packages now and Maven Central later #2 intends.

Stacked on #2. Nothing published, nothing tagged.

…library

Restores the verification half of client.webhooks.unwrap, which the Stainless
SDK shipped and Fern cannot generate: Fern generates from OpenAPI paths and
unwrap was never a path.

Standalone under com.whop.api.helpers rather than a method on WhopApiClient, so
nothing generated has to be patched. javax.crypto.Mac, java.util.Base64 and
java.security.MessageDigest are all in the JDK, so the HMAC is computed over the
raw ws_ secret directly — sidestepping the base64-decoded-key mismatch that made
the Ruby, Python and TypeScript helpers reject every genuine delivery until each
learned to base64-encode the whole secret to cancel it out. No dependency is
added, which matters because build.gradle stays Fern's.

.fernignore was a single comment line and protected nothing; it now keeps the
helper and its tests, and ci.yml fails if either the files or the entries go
missing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XV1533iUUxKJxfn4FXptWz
The e2e suite counts System.getenv references under src/main and asserts the
SDK reads no environment variables. A javadoc example is a match, so the
example now names the secret as a parameter.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XV1533iUUxKJxfn4FXptWz
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