From 2ce93c58a537f7e5f52a91bfb2f29cd175355017 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Fri, 27 Feb 2026 10:13:00 +0100
Subject: [PATCH] Fix authenticated launcher anon removal in Teams hash-routed
meeting URLs.
Remove anon from hash-query form (/_#/meet/...?...&anon=true) so auth sessions no longer drift into anon-like launch behavior.
Made-with: Cursor
---
src/bot/orchestrator.ts | 35 +++++++++++++++++++++++++++--------
1 file changed, 27 insertions(+), 8 deletions(-)
diff --git a/src/bot/orchestrator.ts b/src/bot/orchestrator.ts
index 61a209f..5f9726b 100644
--- a/src/bot/orchestrator.ts
+++ b/src/bot/orchestrator.ts
@@ -120,6 +120,31 @@ export class BotOrchestrator {
return null;
}
+ /**
+ * Teams launcher commonly embeds meeting params in hash-routed paths:
+ * /_#/meet/?p=...&anon=true
+ * In this shape, "anon" is in the hash query (not URL.search).
+ */
+ private _stripAnonFromInnerMeetingUrl(innerUrlPath: string): string {
+ try {
+ const innerUrl = new URL(innerUrlPath, 'https://teams.microsoft.com');
+ innerUrl.searchParams.delete('anon');
+
+ const hash = innerUrl.hash || '';
+ if (hash.includes('?')) {
+ const [hashRoute, hashQuery] = hash.split('?', 2);
+ const hashParams = new URLSearchParams(hashQuery || '');
+ hashParams.delete('anon');
+ const cleanedHashQuery = hashParams.toString();
+ innerUrl.hash = cleanedHashQuery ? `${hashRoute}?${cleanedHashQuery}` : hashRoute;
+ }
+
+ return `${innerUrl.pathname}${innerUrl.search}${innerUrl.hash}`;
+ } catch {
+ return innerUrlPath;
+ }
+ }
+
get sessionId(): string {
return this._sessionId;
}
@@ -289,14 +314,8 @@ export class BotOrchestrator {
// 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
- }
+ const innerPath = this._stripAnonFromInnerMeetingUrl(encodedInnerUrl);
+ urlObj.searchParams.set('url', innerPath);
}
launchUrl = urlObj.toString();
} catch { /* keep as-is */ }