CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-twilio

A comprehensive Node.js SDK for the Twilio communications platform, enabling SMS, voice, video, and other communication services integration.

Pending
Overview
Eval results
Files

webhooks.mddocs/

Webhook Validation

Webhook validation utilities provide security functions to verify that incoming HTTP requests to your application originated from Twilio servers. This prevents unauthorized access and ensures request integrity through cryptographic signature validation.

Capabilities

Request Signature Validation

Core functions for validating Twilio webhook request signatures using HMAC-SHA1 cryptographic verification.

/**
 * Validate that an incoming request originated from Twilio
 * @param authToken - Your Twilio auth token from the console
 * @param twilioSignature - Value of X-Twilio-Signature header from the request
 * @param url - The full webhook URL you configured with Twilio (including query string)
 * @param params - The POST parameters sent with the request
 * @returns true if request is valid, false otherwise
 */
function validateRequest(
  authToken: string,
  twilioSignature: string,
  url: string,
  params: Record<string, any>
): boolean;

/**
 * Validate request signature including body hash verification
 * @param authToken - Your Twilio auth token
 * @param twilioSignature - Value of X-Twilio-Signature header
 * @param url - The full webhook URL (must include bodySHA256 parameter)
 * @param body - The raw request body as a string
 * @returns true if both signature and body hash are valid
 */
function validateRequestWithBody(
  authToken: string,
  twilioSignature: string,
  url: string,
  body: string
): boolean;

/**
 * Validate incoming request using a request object (Express.js compatible)
 * @param request - HTTP request object with headers, body, and URL properties
 * @param authToken - Your Twilio auth token
 * @param opts - Optional configuration for URL construction
 * @returns true if request is valid
 */
function validateIncomingRequest(
  request: Request,
  authToken: string,
  opts?: RequestValidatorOptions
): boolean;

/**
 * Express.js specific request validation (alias for validateIncomingRequest)
 * @param request - Express request object
 * @param authToken - Your Twilio auth token
 * @param opts - Optional validation configuration
 * @returns true if request is valid
 */
function validateExpressRequest(
  request: Request,
  authToken: string,
  opts?: RequestValidatorOptions
): boolean;

Usage Examples:

import TwilioSDK from "twilio";

// Access webhook validation functions
const { validateRequest, validateRequestWithBody } = TwilioSDK;

// Basic webhook validation
app.post("/webhook", (req, res) => {
  const signature = req.headers["x-twilio-signature"];
  const url = "https://myapp.com/webhook";
  const params = req.body;
  
  const isValid = validateRequest(
    process.env.TWILIO_AUTH_TOKEN,
    signature,
    url,
    params
  );
  
  if (!isValid) {
    return res.status(403).send("Invalid signature");
  }
  
  // Process webhook
  res.send("OK");
});

// Validation with body hash (for high-security webhooks)
app.post("/secure-webhook", (req, res) => {
  const signature = req.headers["x-twilio-signature"];
  const url = `https://myapp.com/secure-webhook?bodySHA256=${req.query.bodySHA256}`;
  const body = req.rawBody;
  
  const isValid = validateRequestWithBody(
    process.env.TWILIO_AUTH_TOKEN,
    signature,
    url,
    body
  );
  
  if (isValid) {
    // Process secure webhook
    res.send("OK");
  } else {
    res.status(403).send("Invalid signature or body");
  }
});

Express.js Middleware

Pre-built Express.js middleware for automatic webhook validation with comprehensive error handling.

/**
 * Express.js middleware for Twilio webhook validation and TwiML response helpers
 * @param opts - Configuration options or auth token string
 * @param authToken - Alternative way to provide auth token
 * @returns Express middleware function
 */
function webhook(
  opts?: string | WebhookOptions,
  authToken?: string | WebhookOptions
): (req: any, res: any, next: Function) => void;

interface WebhookOptions {
  /** Whether to validate the request (default: true) */
  validate?: boolean;
  /** Enable TwiML response helpers (default: true) */
  includeHelpers?: boolean;
  /** Full webhook URL (overrides host/protocol) */
  url?: string;
  /** Manually specify hostname for validation */
  host?: string;
  /** Manually specify protocol for validation */
  protocol?: string;
  /** Auth token for validation */
  authToken?: string;
}

Usage Examples:

import express from "express";
import TwilioSDK from "twilio";

const app = express();
const { webhook } = TwilioSDK;

// Basic webhook middleware with environment variable auth token
app.use("/webhooks", webhook());

// Webhook middleware with explicit auth token
app.use("/webhooks", webhook(process.env.TWILIO_AUTH_TOKEN));

// Webhook middleware with custom configuration
app.use("/webhooks", webhook({
  validate: true,
  host: "myapp.ngrok.io",
  protocol: "https",
  authToken: process.env.TWILIO_AUTH_TOKEN
}));

// Webhook middleware without validation (for development)
app.use("/dev-webhooks", webhook({ validate: false }));

// Route handler after middleware
app.post("/webhooks/voice", (req, res) => {
  // Request is already validated by middleware
  const response = new VoiceResponse();
  response.say("Hello from validated webhook!");
  res.type("text/xml").send(response.toString());
});

Request Validation Configuration

Configuration options for fine-tuning webhook validation behavior.

interface RequestValidatorOptions {
  /** Full webhook URL with query string (overrides host/protocol) */
  url?: string;
  /** Hostname used by Twilio in webhook configuration */
  host?: string;
  /** Protocol used by Twilio in webhook configuration */
  protocol?: string;
}

interface Request {
  /** Request protocol (http/https) */
  protocol: string;
  /** Get header value by name */
  header(name: string): string | undefined;
  /** All request headers */
  headers: IncomingHttpHeaders;
  /** Original request URL with query string */
  originalUrl: string;
  /** Raw request body (for body hash validation) */
  rawBody?: any;
  /** Parsed request body parameters */
  body: any;
}

Signature Generation Utilities

Low-level utilities for generating and comparing webhook signatures.

/**
 * Generate the expected signature for a given request
 * @param authToken - Your Twilio auth token
 * @param url - The full webhook URL with query parameters
 * @param params - The request parameters
 * @returns Expected signature string (base64 encoded)
 */
function getExpectedTwilioSignature(
  authToken: string,
  url: string,
  params: Record<string, any>
): string;

/**
 * Generate the expected SHA256 hash for a request body
 * @param body - The plain-text request body
 * @returns Expected body hash (hex encoded)
 */
function getExpectedBodyHash(body: string): string;

/**
 * Validate request body against provided hash
 * @param body - The request body string
 * @param bodyHash - The hash to validate against
 * @returns true if body hash matches
 */
function validateBody(
  body: string,
  bodyHash: string | Buffer | any[]
): boolean;

Usage Examples:

// Manual signature verification
const expectedSignature = getExpectedTwilioSignature(
  authToken,
  "https://myapp.com/webhook?foo=bar",
  { From: "+1234567890", Body: "Hello" }
);

const providedSignature = req.headers["x-twilio-signature"];
const isValid = expectedSignature === providedSignature;

// Body hash verification
const bodyHash = getExpectedBodyHash(req.rawBody);
const isBodyValid = validateBody(req.rawBody, req.query.bodySHA256);

Advanced Validation Scenarios

Handle complex webhook validation scenarios including proxy servers, load balancers, and custom URL configurations.

Usage Examples:

// Validation behind reverse proxy
app.post("/webhook", (req, res) => {
  const isValid = validateIncomingRequest(req, authToken, {
    protocol: "https", // Force HTTPS even if proxy forwards HTTP
    host: "myapp.com"   // Use external hostname
  });
});

// Validation with custom webhook URL
app.post("/webhook", (req, res) => {
  const isValid = validateIncomingRequest(req, authToken, {
    url: "https://myapp.com/webhook?custom=param"
  });
});

// Multiple validation attempts for different URL formats
function validateWebhook(req, authToken) {
  const signature = req.headers["x-twilio-signature"];
  
  // Try different URL variations that Twilio might use
  const urls = [
    `https://${req.headers.host}${req.originalUrl}`,
    `https://${req.headers.host}:443${req.originalUrl}`,
    `http://${req.headers.host}${req.originalUrl}`,
    `http://${req.headers.host}:80${req.originalUrl}`
  ];
  
  return urls.some(url => 
    validateRequest(authToken, signature, url, req.body)
  );
}

Security Best Practices

Important security considerations when implementing webhook validation.

Usage Examples:

// Always validate in production
const isProduction = process.env.NODE_ENV === "production";

app.use("/webhooks", webhook({
  validate: isProduction, // Only skip validation in development
  authToken: process.env.TWILIO_AUTH_TOKEN
}));

// Secure error handling
app.post("/webhook", (req, res) => {
  try {
    const isValid = validateRequest(/* ... */);
    
    if (!isValid) {
      // Log failed validation attempts
      console.warn("Invalid webhook signature", {
        ip: req.ip,
        userAgent: req.headers["user-agent"],
        timestamp: new Date().toISOString()
      });
      
      return res.status(403).send("Forbidden");
    }
    
    // Process valid webhook...
    
  } catch (error) {
    console.error("Webhook validation error:", error);
    res.status(500).send("Internal Server Error");
  }
});

// Rate limiting validation failures
const rateLimit = require("express-rate-limit");

const validationFailureLimit = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // limit each IP to 5 failed validations per windowMs
  skip: (req) => {
    // Only count failed validations
    const isValid = validateIncomingRequest(req, authToken);
    return isValid;
  },
  message: "Too many invalid webhook attempts"
});

app.use("/webhooks", validationFailureLimit);

Types

interface IncomingHttpHeaders {
  [key: string]: string | string[] | undefined;
}

type ValidationResult = boolean;

interface SignatureComponents {
  /** The webhook URL used for signature generation */
  url: string;
  /** The request parameters included in signature */
  params: Record<string, any>;
  /** The computed signature hash */
  signature: string;
}

Install with Tessl CLI

npx tessl i tessl/npm-twilio

docs

credential-providers.md

index.md

jwt.md

rest-client.md

twiml.md

webhooks.md

tile.json