Skip to content

Commit a51fc80

Browse files
Mohamed Elghobatygh-workflow-token-generator[bot]
andauthored
Verify webhook signature and update webhook schema and types (#156)
* verify webhook signature + update webhook schema and types * update return error type & readme --------- Co-authored-by: gh-workflow-token-generator[bot] <104082997+gh-workflow-token-generator[bot]@users.noreply.github.com>
1 parent f8789e4 commit a51fc80

23 files changed

+1024
-272
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@team-plain/typescript-sdk': major
3+
---
4+
5+
Upgrade the SDK webhook parsing to support webhook version '2024-09-18'.
6+
7+
### Breaking Changes
8+
If your Plain webhook target is on the legacy/unversioned version, the SDK will no longer be able to parse the webhook payload. You must update your webhook target to '2024-09-18'. Refer to [our docs](https://www.plain.com/docs/api-reference/webhooks/versions) for more information and instructions on how to safely upgrade your webhook target.
9+
10+
### Added
11+
`verifyPlainWebhook` method to verify the webhook signature, with support for replay attack protection, and parse the webhook payload. Refer to the [README](./README.md) for usage instructions.

README.md

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# @team-plain/typescript-sdk
22

3-
[Changelog]('./CHANGELOG.md')
3+
[Changelog](./CHANGELOG.md)
44

55
## Plain Client
66

@@ -104,16 +104,40 @@ Fallback error type when something unexpected happens.
104104

105105
## Webhooks
106106

107-
This package also provides functionality to validate our [Webhook payloads](https://www.plain.com/docs/api-reference/webhooks).
107+
Plain signs the [webhooks](https://www.plain.com/docs/api-reference/webhooks) it sends to your endpoint,
108+
allowing you to validate that they were not sent by a third-party. You can read more about it [here](https://www.plain.com/docs/api-reference/request-signing).
109+
The SDK provides a convenient helper function to verify the signature, prevent replay attacks, and parse the payload to a typed object.
108110

109111
```ts
110-
import { parsePlainWebhook } from '@team-plain/typescript-sdk';
111-
112-
const payload = { ... };
113-
114-
if(parsePlainWebhook(payload)) {
115-
// payload is now typed!
116-
doYourThing(payload);
112+
import {
113+
PlainWebhookSignatureVerificationError,
114+
PlainWebhookVersionMismatchError,
115+
verifyPlainWebhook,
116+
} from '@team-plain/typescript-sdk';
117+
118+
// You must pass the raw request body, exactly as received from Plain,
119+
// this will not work with a parsed (i.e., JSON) request body.
120+
const payload = '...';
121+
122+
// The value of the `Plain-Request-Signature` header from the webhook request.
123+
const signature = '...';
124+
125+
// Plain Request Signature Secret. You can find this in Plain's settings.
126+
const secret = '...';
127+
128+
const webhookResult = verifyPlainWebhook(payload, signature, secret);
129+
if (webhookResult.error instanceof PlainWebhookSignatureVerificationError) {
130+
// Signature verification failed.
131+
} else if (webhookResult.error instanceof PlainWebhookVersionMismatchError) {
132+
// The SDK is not compatible with the received webhook version.
133+
// Consider updating the SDK and the webhook target to the latest version.
134+
// Consult the changelog or https://plain.com/docs/api-reference/webhooks/versions for more information.
135+
} else if (webhookResult.error) {
136+
// Unexpected error. Most likely due to an error in Plain's webhook server or a bug in the SDK.
137+
// Treat this as a 500 response from Plain.
138+
// We also recommend logging the error and sharing it with Plain's support team.
139+
} else {
140+
// webhookResult.data is now a typed object.
117141
}
118142
```
119143

biome.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@
2323
}
2424
},
2525
"files": {
26-
"include": ["biome.json", "vitest.*.js", "src/**/*.ts", "src/**/*.gql"],
26+
"include": [
27+
"biome.json",
28+
"vitest.*.js",
29+
"src/**/*.ts",
30+
"src/**/*.gql"
31+
],
2732
"ignore": [
2833
"dist/**",
2934
"node_modules/**",
@@ -59,4 +64,4 @@
5964
"organizeImports": {
6065
"enabled": true
6166
}
62-
}
67+
}

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
"@graphql-codegen/typescript-document-nodes": "^3.0.4",
3232
"@graphql-codegen/typescript-operations": "^3.0.4",
3333
"@rollup/plugin-json": "^6.1.0",
34+
"@types/lodash.get": "^4.4.9",
35+
"@types/node": "^22.7.2",
3436
"esbuild": "^0.17.18",
3537
"json-schema-to-typescript": "^13.1.2",
3638
"rollup": "^3.21.5",
@@ -46,6 +48,7 @@
4648
"ajv": "^8.12.0",
4749
"ajv-formats": "^2.1.1",
4850
"graphql": "^16.6.0",
51+
"lodash.get": "^4.4.2",
4952
"zod": "3.22.4"
5053
}
5154
}

pnpm-lock.yaml

Lines changed: 55 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)