Estoy tratando de hacer un servidor webhook de GitHub con Deno, pero no puedo encontrar ninguna forma posible de hacer la validación.
Este es mi intento actual de usar webhooks-methods.js :
import { Application } from "https://deno.land/x/oak/mod.ts"; import { verify } from "https://cdn.skypack.dev/@octokit/webhooks-methods?dts"; const app = new Application(); app.use(async (ctx, next) => { try { await next(); } catch (_err) { ctx.response.status = 500; } }); const secret = "..."; app.use(async (ctx) => { const signature = ctx.request.headers.get("X-Hub-Signature-256"); if (signature) { const payload = await ctx.request.body({ type: "text" }).value; const result = await verify(secret, payload, signature); console.log(result); } ctx.response.status = 200; }); La función verify devuelve false cada vez.
Tu ejemplo es muy cercano. La documentación del webhook de GitHub detalla el esquema del encabezado de la firma. El valor es un prefijo de algoritmo de resumen seguido de la firma, en el formato de ${ALGO}=${SIGNATURE} :
X-Hub-Signature-256: sha256=d57c68ca6f92289e6987922ff26938930f6e66a2d161ef06abdf1859230aa23cPor lo tanto, debe extraer la firma del valor (omitiendo el prefijo):
const signatureHeader = request.headers.get('X-Hub-Signature-256'); const signature = signatureHeader.slice('sha256='.length);Aquí hay un ejemplo de trabajo completo que puede simplemente copiar y pegar en un área de juegos o proyecto en Deno Deploy :
gh-webhook-logger.ts :
import {assert} from 'https://deno.land/std@0.132.0/testing/asserts.ts'; import {Application, Router, NativeRequest} from 'https://deno.land/x/oak@v10.5.1/mod.ts'; import type {ServerRequest} from 'https://deno.land/x/oak@v10.5.1/types.d.ts'; import {verify} from 'https://raw.githubusercontent.com/octokit/webhooks-methods.js/v2.0.0/src/web.ts'; // In actual usage, use a private secret: // const SECRET = Deno.env.get('SIGNING_SECRET'); // But for the purposes of this demo, the exposed secret is: const SECRET = 'Let me know if you found this to be helpful!'; type GitHubWebhookVerificationStatus = { id: string; verified: boolean; } // Because this uses a native Request, it can be used in other contexts besides Oak (eg `std/http/serve`) async function verifyGitHubWebhook (request: Request): Promise<GitHubWebhookVerificationStatus> { const id = request.headers.get('X-GitHub-Delivery'); // This should be more strict in reality if (!id) throw new Error('Not a GH webhhok'); const signatureHeader = request.headers.get('X-Hub-Signature-256'); let verified = false; if (signatureHeader) { const signature = signatureHeader.slice('sha256='.length); const payload = await request.clone().text(); verified = await verify(SECRET, payload, signature); } return {id, verified}; } // Type predicate used to access native Request instance // Ref: https://github.com/oakserver/oak/issues/501#issuecomment-1084046581 function isNativeRequest (r: ServerRequest): r is NativeRequest { // deno-lint-ignore no-explicit-any return (r as any).request instanceof Request; } const webhookLogger = new Router().post('/webhook', async (ctx) => { assert(isNativeRequest(ctx.request.originalRequest)); const status = await verifyGitHubWebhook(ctx.request.originalRequest.request); console.log(status); ctx.response.status = 200; }); const app = new Application() .use(webhookLogger.routes()) .use(webhookLogger.allowedMethods()); // The port is not important in Deno Deploy await app.listen({port: 8080});