Skip to content

Commit

Permalink
Pim 1306 1 (#3)
Browse files Browse the repository at this point in the history
* Update webhook

* Update

* Disable sideEffects

* Update pnpm workspace

* Add changeset

* Remove packageManager

* chore: format
  • Loading branch information
pavlovdog authored Dec 3, 2024
1 parent ed73df4 commit 8a85979
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 150 deletions.
6 changes: 6 additions & 0 deletions .changeset/nice-parents-rush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@pimlico/example": patch
"@pimlico/webhook": patch
---

Added webhook secret support
10 changes: 6 additions & 4 deletions examples/webhook/api/approve.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { pimlicoWebhookVerifier } from "@pimlico/webhook"
import type { VercelRequest, VercelResponse } from "@vercel/node"

const apiKey = process.env.PIMLICO_API_KEY as string
const webhookSecret = process.env.PIMLICO_WEBHOOK_SECRET as string

const verifyWebhook = pimlicoWebhookVerifier(apiKey)
const verifyWebhook = pimlicoWebhookVerifier(webhookSecret)

export default async function handler(req: VercelRequest, res: VercelResponse) {
const body = await verifyWebhook(
const webhookEvent = verifyWebhook(
req.headers as Record<string, string>,
Buffer.from(JSON.stringify(req.body))
JSON.stringify(req.body)
)

// console.log(webhookEvent.data.object.userOperation)

return res.status(200).json({
sponsor: true
})
Expand Down
2 changes: 1 addition & 1 deletion examples/webhook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"license": "ISC",
"private": true,
"dependencies": {
"@pimlico/webhook": "workspace:*",
"@pimlico/webhook": "workspace:^0.0.2",
"@vercel/node": "^3.1.7",
"vercel": "^34.2.7"
}
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"name": "pimlico-webhook",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
Expand All @@ -7,7 +9,7 @@
"format": "biome format --write .",
"webhook": "pnpm --filter @pimlico/webhook",
"build": "pnpm webhook build",
"clean": "rimraf ./packages/*/node_modules ./packages/*/_cjs ./packages/*/_esm ./packages/*/_types ./packages/*/dist ./packages/*/node_modules ./node_modules",
"clean": "rimraf ./packages/*/node_modules ./packages/*/_cjs ./packages/*/_esm ./packages/*/_types ./packages/*/dist ./packages/*/node_modules ./node_modules ./examples/*/node_modules",
"changeset": "changeset",
"changeset:release": "pnpm build && changeset publish",
"changeset:version": "changeset version && pnpm install --lockfile-only"
Expand All @@ -21,6 +23,7 @@
},
"type": "module",
"devDependencies": {
"tslib": "^2.6.2",
"@biomejs/biome": "1.9.4",
"@changesets/changelog-git": "^0.2.0",
"@changesets/changelog-github": "^0.5.0",
Expand Down
1 change: 1 addition & 0 deletions packages/webhook/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./src"
11 changes: 4 additions & 7 deletions packages/webhook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,6 @@
"publishConfig": {
"access": "public"
},
"exports": {
".": {
"types": "./_types/index.d.ts",
"import": "./_esm/index.js",
"default": "./_cjs/index.js"
}
},
"scripts": {
"build": "pnpm build:cjs && pnpm build:esm",
"build:cjs": "tsc --project ./../../tsconfig/tsconfig.webhook.cjs.json && tsc-alias -p ./../../tsconfig/tsconfig.webhook.cjs.json && printf '{\"type\":\"commonjs\"}' > ./_cjs/package.json",
Expand All @@ -31,5 +24,9 @@
},
"peerDependencies": {
"viem": "^2.21.52"
},
"dependencies": {
"base-x": "^5.0.0",
"svix": "^1.42.0"
}
}
48 changes: 18 additions & 30 deletions packages/webhook/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,28 @@
import * as crypto from "node:crypto"
import { keyFetcher } from "./key-fetcher"
import basex from "base-x"
import { Webhook } from "svix"
import type { PimlicoSponsorshipPolicyWebhookBody } from "./types"

export const pimlicoWebhookVerifier =
(apiKey: string) =>
async (headers: Record<string, string>, body: Buffer) => {
const fetchKey = keyFetcher(apiKey)
const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvxyz" // no 'w', it's used for padding only

if (!Buffer.isBuffer(body)) {
throw new Error("expected body to be a Buffer")
}

if (process.env.UNSAFE_SKIP_WEBHOOK_VERIFY != null) {
return JSON.parse(
body.toString()
) as PimlicoSponsorshipPolicyWebhookBody
}
const bs58 = basex(ALPHABET)

if (!body || body.length === 0) {
throw new Error("invalid webhook: empty payload")
}
export const pimlicoWebhookVerifier = (webhookSecret: string) => {
const webhookSecretHex = Buffer.from(
bs58.decode(webhookSecret.replace("pim_whsec_", ""))
).toString("hex")

const key = await fetchKey()
const verify = (headers: Record<string, string>, payload: string) => {
const wh = new Webhook(webhookSecretHex)

const signature = Buffer.from(
headers["pimlico-signature"] ?? "",
"base64"
)

const message = Buffer.concat([body])

if (!crypto.verify("sha256", message, key, signature)) {
throw new Error("invalid webhook: signature validation failed")
if (!payload || payload.length === 0) {
throw new Error("invalid webhook: empty payload")
}

return JSON.parse(
body.toString()
return wh.verify(
payload,
headers
) as PimlicoSponsorshipPolicyWebhookBody
}

return verify
}
70 changes: 0 additions & 70 deletions packages/webhook/src/key-fetcher.ts

This file was deleted.

15 changes: 0 additions & 15 deletions packages/webhook/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,5 @@
import type * as crypto from "node:crypto"
import type { Address } from "viem"
import type { UserOperation } from "viem/account-abstraction"
type Fetch = typeof fetch

export type KeyFetcher = () => Promise<crypto.KeyObject>

export interface KeyFetcherOptions {
baseURL?: string
cache?: KeyCache
fetch?: Fetch
}

export interface KeyCache {
get(k: string): crypto.KeyObject | null | undefined
set(k: string, v: crypto.KeyObject): void
}

export type PimlicoWebhookBody = PimlicoSponsorshipPolicyWebhookBody

Expand Down
Loading

0 comments on commit 8a85979

Please sign in to comment.