mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-11 02:54:31 +00:00
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:
@@ -146,6 +146,11 @@ export class MediaStreamHandler {
|
|||||||
const streamSid = message.streamSid || "";
|
const streamSid = message.streamSid || "";
|
||||||
const callSid = message.start?.callSid || "";
|
const callSid = message.start?.callSid || "";
|
||||||
|
|
||||||
|
// Prefer token from start message customParameters (set via TwiML <Parameter>),
|
||||||
|
// falling back to query string token. Twilio strips query params from WebSocket
|
||||||
|
// URLs but reliably delivers <Parameter> values in customParameters.
|
||||||
|
const effectiveToken = message.start?.customParameters?.token ?? streamToken;
|
||||||
|
|
||||||
console.log(`[MediaStream] Stream started: ${streamSid} (call: ${callSid})`);
|
console.log(`[MediaStream] Stream started: ${streamSid} (call: ${callSid})`);
|
||||||
if (!callSid) {
|
if (!callSid) {
|
||||||
console.warn("[MediaStream] Missing callSid; closing stream");
|
console.warn("[MediaStream] Missing callSid; closing stream");
|
||||||
@@ -154,7 +159,7 @@ export class MediaStreamHandler {
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
this.config.shouldAcceptStream &&
|
this.config.shouldAcceptStream &&
|
||||||
!this.config.shouldAcceptStream({ callId: callSid, streamSid, token: streamToken })
|
!this.config.shouldAcceptStream({ callId: callSid, streamSid, token: effectiveToken })
|
||||||
) {
|
) {
|
||||||
console.warn(`[MediaStream] Rejecting stream for unknown call: ${callSid}`);
|
console.warn(`[MediaStream] Rejecting stream for unknown call: ${callSid}`);
|
||||||
ws.close(1008, "Unknown call");
|
ws.close(1008, "Unknown call");
|
||||||
@@ -393,6 +398,7 @@ interface TwilioMediaMessage {
|
|||||||
accountSid: string;
|
accountSid: string;
|
||||||
callSid: string;
|
callSid: string;
|
||||||
tracks: string[];
|
tracks: string[];
|
||||||
|
customParameters?: Record<string, string>;
|
||||||
mediaFormat: {
|
mediaFormat: {
|
||||||
encoding: string;
|
encoding: string;
|
||||||
sampleRate: number;
|
sampleRate: number;
|
||||||
|
|||||||
@@ -429,10 +429,21 @@ export class TwilioProvider implements VoiceCallProvider {
|
|||||||
* @param streamUrl - WebSocket URL (wss://...) for the media stream
|
* @param streamUrl - WebSocket URL (wss://...) for the media stream
|
||||||
*/
|
*/
|
||||||
getStreamConnectXml(streamUrl: string): string {
|
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"?>
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Response>
|
<Response>
|
||||||
<Connect>
|
<Connect>
|
||||||
<Stream url="${escapeXml(streamUrl)}" />
|
<Stream url="${escapeXml(cleanUrl)}">${paramXml}
|
||||||
|
</Stream>
|
||||||
</Connect>
|
</Connect>
|
||||||
</Response>`;
|
</Response>`;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user