SUPPORT.TWILIO.COM END OF LIFE NOTICE: This site, support.twilio.com, is scheduled to go End of Life on February 27, 2024. All Twilio Support content has been migrated to help.twilio.com, where you can continue to find helpful Support articles, API docs, and Twilio blog content, and escalate your issues to our Support team. We encourage you to update your bookmarks and begin using the new site today for all your Twilio Support needs.

How to Access a Protected Function From Outside of Twilio

Objective

This article explains how to access a Protected Function from outside of Twilio (e.g., from your own web application or a curl request).

 

Product

Twilio Functions

 

Environment

legacy Twilio Console

 

Procedure 

Requirement

A protected Function or Asset requires a valid Twilio X-Twilio-Signature header in the request in order to be accessed. 

This extra layer of protection makes Protected Assets particularly useful for storing sensitive information that needs to be referenced only by your code but not accessible by the public.

 

Using a Valid Twilio Request Signature

A Protected Function checks for the X-Twilio-Signature HTTP header in the incoming request. This signature is a hash generated using the request URL, parameters, and your Twilio Auth Token. If the signature is missing or invalid, the function will return a 403 error.

This is the standard and most secure way to access a protected function from a non-Twilio service you control.

- You must generate the signature on your server using your Twilio Auth Token and the full request details.

- The signature generation process involves:

  1. Taking the full request URL (including any query parameters).
  2. For POST requests, sorting all POST parameters alphabetically and appending the variable name and value (with no delimiters) to the URL string.
  3. Signing the resulting string with HMAC-SHA1 using your Auth Token as the key.
  4. Base64 encoding the resulting hash value.

- You then include this Base64-encoded value in the X-Twilio-Signature header of your request.

 

Node.js Example: Generating the Twilio Signature

const crypto = require('crypto');
const axios = require('axios');
/**
 * Generates the X-Twilio-Signature for a request.
 * * @param {string} authToken Your Twilio Auth Token.
 * @param {string} url The full URL the request is being sent to (including protocol and query string).
 * @param {object} params A dictionary of POST parameters (or an empty object for GET requests).
 * @returns {string} The Base64 encoded Twilio signature.
 */
function generateTwilioSignature(authToken, url, params = {}) {
    // 1. Start with the full request URL
    let signatureString = url;

    // 2. Append sorted POST parameters (key and value, no delimiters)
    const sortedKeys = Object.keys(params).sort();
    for (const key of sortedKeys) {
        // Twilio expects string values, so ensure numbers are converted.
        const value = params[key].toString(); 
        signatureString += key + value;
    }

    // 3. Sign the string with HMAC-SHA1 using your Auth Token as the key
    const hash = crypto.createHmac('sha1', authToken)
                       .update(signatureString)
                       .digest('base64');

    // 4. Return the Base64 encoded hash
    return hash;
}

// --- Example Usage ---

// NOTE: Replace with your actual Twilio Auth Token and Function URL
const TWILIO_AUTH_TOKEN = 'your_twilio_auth_token'; 
const FUNCTION_URL = 'https://your-domain.twil.io/your-protected-function';

// Parameters you will send in the POST body of your request
const requestParameters = {
    Message: 'Hello from my server!',
    From: 'from_number',
    To: 'to_number'
};

const signature = generateTwilioSignature(
    TWILIO_AUTH_TOKEN, 
    FUNCTION_URL, 
    requestParameters
);

console.log(`\nGenerated Signature: ${signature}`);
console.log(`\nTo make the request, you would set the header:\n'X-Twilio-Signature': '${signature}'`);

// --- Making the Request (using 'axios' for demonstration) ---

async function callProtectedFunction() {
    try {
        const response = await axios.post(FUNCTION_URL, requestParameters, {
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded', // Twilio expects this type for webhooks
                'X-Twilio-Signature': signature
            }
        });
        console.log('\nTwilio Function Response Status:', response.status);
        console.log('Twilio Function Response Body:', response.data);
    } catch (error) {
        console.error('Error calling Twilio Function:', error.response ? error.response.status : error.message);
    }
}

 callProtectedFunction();

 

Validating Requests are coming from Twilio

To Note:

If you are using the default "Protected" visibility for a Twilio Function, you do not need to write this code. Twilio automatically performs this check before your function code runs, and if the check fails, it stops the request and returns a 403 Forbidden status.

You only need this verification code if:

- You set the Function's visibility to Public but still want signature protection.

- You are using an external service (like AWS Lambda or your own Express server) to handle Twilio webhooks.

 

You can use the official Twilio Node.js helper library for the easiest and most reliable verification.

const client = require('twilio');

/**
 * Validates an incoming Twilio request using the Twilio Request Validator with x-www-form-urlencoded body
 * @param {string} authToken Your Twilio Auth Token (from your environment variables).
 * @param {string} signature The value of the X-Twilio-Signature header from the incoming request.
 * @param {string} url The full URL the request was sent to (including protocol and query string).
 * @param {object} params A dictionary of POST parameters (or an empty object for GET requests).
 * @returns {boolean} True if the signature is valid, false otherwise.
 */
function verifyTwilioSignature(authToken, signature, url, params = {}) {
    return client.validateRequest(authToken, signature, url, params);
}

// --- Example Usage (Inside your Protected Function) ---

// 1. Get these values from the incoming request object (e.g., req.headers, req.body)
const TWILIO_AUTH_TOKEN = process.env.TWILIO_AUTH_TOKEN; // Always use environment variables!
const INCOMING_SIGNATURE = 'rW+40V0uWjL/x1HlVfPjR/P5k2s='; // Example value from X-Twilio-Signature header
const INCOMING_URL = 'https://your-domain.twil.io/your-protected-function'; // Full request URL

// 2. The parameters sent in the request body (e.g., in Node.js Express: req.body)
const INCOMING_PARAMETERS = {
    Message: 'Hello from my server!',
    From: 'from_number',
    To: 'to_number'
};

const isRequestValid = verifyTwilioSignature(
    TWILIO_AUTH_TOKEN, 
    INCOMING_SIGNATURE, 
    INCOMING_URL, 
    INCOMING_PARAMETERS
);

console.log(`\nIs the request valid? ${isRequestValid}`);

if (isRequestValid) {
    // Proceed with Function logic
    console.log('Signature validated successfully. Processing request...');
} else {
    // Reject the request
    console.log('WARNING: Invalid Twilio signature. Rejecting request with 403 Forbidden.');
}

 

Additional Information

Have more questions? Submit a request
Powered by Zendesk