fix(voice-call): harden inbound policy

This commit is contained in:
Peter Steinberger
2026-02-03 09:33:25 -08:00
parent fc40ba8e7e
commit f8dfd034f5
13 changed files with 328 additions and 33 deletions

View File

@@ -21,15 +21,21 @@ import type { VoiceCallProvider } from "./base.js";
* Uses Telnyx Call Control API v2 for managing calls.
* @see https://developers.telnyx.com/docs/api/v2/call-control
*/
export interface TelnyxProviderOptions {
/** Allow unsigned webhooks when no public key is configured */
allowUnsignedWebhooks?: boolean;
}
export class TelnyxProvider implements VoiceCallProvider {
readonly name = "telnyx" as const;
private readonly apiKey: string;
private readonly connectionId: string;
private readonly publicKey: string | undefined;
private readonly options: TelnyxProviderOptions;
private readonly baseUrl = "https://api.telnyx.com/v2";
constructor(config: TelnyxConfig) {
constructor(config: TelnyxConfig, options: TelnyxProviderOptions = {}) {
if (!config.apiKey) {
throw new Error("Telnyx API key is required");
}
@@ -40,6 +46,7 @@ export class TelnyxProvider implements VoiceCallProvider {
this.apiKey = config.apiKey;
this.connectionId = config.connectionId;
this.publicKey = config.publicKey;
this.options = options;
}
/**
@@ -76,8 +83,14 @@ export class TelnyxProvider implements VoiceCallProvider {
*/
verifyWebhook(ctx: WebhookContext): WebhookVerificationResult {
if (!this.publicKey) {
// No public key configured, skip verification (not recommended for production)
return { ok: true };
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)",
};
}
const signature = ctx.headers["telnyx-signature-ed25519"];