fix(voice-call): fail closed when Telnyx webhook public key missing

This commit is contained in:
Peter Steinberger
2026-02-14 18:13:44 +01:00
parent ff11d8793b
commit 29b587e73c
8 changed files with 75 additions and 15 deletions

View File

@@ -0,0 +1,47 @@
import { describe, expect, it } from "vitest";
import type { WebhookContext } from "../types.js";
import { TelnyxProvider } from "./telnyx.js";
function createCtx(params?: Partial<WebhookContext>): WebhookContext {
return {
headers: {},
rawBody: "{}",
url: "http://localhost/voice/webhook",
method: "POST",
query: {},
remoteAddress: "127.0.0.1",
...params,
};
}
describe("TelnyxProvider.verifyWebhook", () => {
it("fails closed when public key is missing and skipVerification is false", () => {
const provider = new TelnyxProvider(
{ apiKey: "KEY123", connectionId: "CONN456", publicKey: undefined },
{ skipVerification: false },
);
const result = provider.verifyWebhook(createCtx());
expect(result.ok).toBe(false);
});
it("allows requests when skipVerification is true (development only)", () => {
const provider = new TelnyxProvider(
{ apiKey: "KEY123", connectionId: "CONN456", publicKey: undefined },
{ skipVerification: true },
);
const result = provider.verifyWebhook(createCtx());
expect(result.ok).toBe(true);
});
it("fails when signature headers are missing (with public key configured)", () => {
const provider = new TelnyxProvider(
{ apiKey: "KEY123", connectionId: "CONN456", publicKey: "public-key" },
{ skipVerification: false },
);
const result = provider.verifyWebhook(createCtx({ headers: {} }));
expect(result.ok).toBe(false);
});
});

View File

@@ -22,8 +22,8 @@ import type { VoiceCallProvider } from "./base.js";
* @see https://developers.telnyx.com/docs/api/v2/call-control
*/
export interface TelnyxProviderOptions {
/** Allow unsigned webhooks when no public key is configured */
allowUnsignedWebhooks?: boolean;
/** Skip webhook signature verification (development only, NOT for production) */
skipVerification?: boolean;
}
export class TelnyxProvider implements VoiceCallProvider {
@@ -82,11 +82,12 @@ export class TelnyxProvider implements VoiceCallProvider {
* Verify Telnyx webhook signature using Ed25519.
*/
verifyWebhook(ctx: WebhookContext): WebhookVerificationResult {
if (this.options.skipVerification) {
console.warn("[telnyx] Webhook verification skipped (skipSignatureVerification=true)");
return { ok: true, reason: "verification skipped (skipSignatureVerification=true)" };
}
if (!this.publicKey) {
if (this.options.allowUnsignedWebhooks) {
console.warn("[telnyx] Webhook verification skipped (no public key configured)");
return { ok: true, reason: "verification skipped (no public key configured)" };
}
return {
ok: false,
reason: "Missing telnyx.publicKey (configure to verify webhooks)",