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 */ }