From 3eaebae2a3ad84f8ab154b241633de04e7511d60 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Fri, 27 Feb 2026 07:56:49 +0100
Subject: [PATCH] Fix authenticated launch URL anon leak and add audio override
diagnostics.
Remove lingering anon=true from encoded launcher url parameter in auth mode and log getUserMedia override activation details to verify Teams receives the injected audio stream.
Made-with: Cursor
---
src/bot/audioProcedure.ts | 11 +++++++++++
src/bot/orchestrator.ts | 13 +++++++++++++
2 files changed, 24 insertions(+)
diff --git a/src/bot/audioProcedure.ts b/src/bot/audioProcedure.ts
index cbc573b..95d4565 100644
--- a/src/bot/audioProcedure.ts
+++ b/src/bot/audioProcedure.ts
@@ -68,6 +68,17 @@ export class AudioProcedure {
// Keep the real video tracks (from fake camera)
realStream.getVideoTracks().forEach(t => combinedStream.addTrack(t));
+ // Diagnostic signal for production logs: confirms override really feeds Teams.
+ try {
+ const audioTracks = combinedStream.getAudioTracks();
+ const videoTracks = combinedStream.getVideoTracks();
+ console.log(
+ `[AudioPlayback] getUserMedia override active: audioTracks=${audioTracks.length}, videoTracks=${videoTracks.length}, audioLabel="${audioTracks[0]?.label || 'n/a'}"`,
+ );
+ } catch {
+ // ignore
+ }
+
return combinedStream;
}
diff --git a/src/bot/orchestrator.ts b/src/bot/orchestrator.ts
index 37631c1..17cac1d 100644
--- a/src/bot/orchestrator.ts
+++ b/src/bot/orchestrator.ts
@@ -286,6 +286,19 @@ export class BotOrchestrator {
try {
const urlObj = new URL(launchUrl);
urlObj.searchParams.delete('anon');
+ // Some Teams launcher URLs carry the real meeting path in an encoded "url" param.
+ // In auth mode that inner URL can still contain anon=true, which forces guest-like behavior.
+ const encodedInnerUrl = urlObj.searchParams.get('url');
+ if (encodedInnerUrl) {
+ try {
+ const inner = new URL(encodedInnerUrl, 'https://teams.microsoft.com');
+ inner.searchParams.delete('anon');
+ const innerPath = `${inner.pathname}${inner.search}${inner.hash}`;
+ urlObj.searchParams.set('url', innerPath);
+ } catch {
+ // keep original inner URL if parsing fails
+ }
+ }
launchUrl = urlObj.toString();
} catch { /* keep as-is */ }