fix(voice-call): pass Twilio stream auth token via <Parameter> instead of query string (#14029)

Twilio strips query parameters from WebSocket URLs in <Stream> TwiML,
so the auth token set via ?token=xxx never arrives on the WebSocket
connection. This causes stream rejection when token validation is enabled.

Fix: pass the token as a <Parameter> element inside <Stream>, which
Twilio delivers in the start message's customParameters field. The
media stream handler now extracts the token from customParameters,
falling back to query string for backwards compatibility.

Co-authored-by: McWiggles <mcwigglesmcgee@users.noreply.github.com>
This commit is contained in:
mcwigglesmcgee
2026-02-12 05:55:00 -08:00
committed by GitHub
parent f8c91b3c5f
commit f8cad44cd6
2 changed files with 19 additions and 2 deletions

View File

@@ -429,10 +429,21 @@ export class TwilioProvider implements VoiceCallProvider {
* @param streamUrl - WebSocket URL (wss://...) for the media stream
*/
getStreamConnectXml(streamUrl: string): string {
// Extract token from URL and pass via <Parameter> instead of query string.
// Twilio strips query params from WebSocket URLs, but delivers <Parameter>
// values in the "start" message's customParameters field.
const parsed = new URL(streamUrl);
const token = parsed.searchParams.get("token");
parsed.searchParams.delete("token");
const cleanUrl = parsed.toString();
const paramXml = token ? `\n <Parameter name="token" value="${escapeXml(token)}" />` : "";
return `<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Connect>
<Stream url="${escapeXml(streamUrl)}" />
<Stream url="${escapeXml(cleanUrl)}">${paramXml}
</Stream>
</Connect>
</Response>`;
}