From 4b6adaad8d006be568bfe199eee9b7979524d829 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Mon, 16 Feb 2026 14:01:52 +0100
Subject: [PATCH] fix: preserve Teams return URL during auth login - skip goto
when already on login page, poll for redirect completion
Co-authored-by: Cursor
---
src/bot/authProcedure.ts | 20 +++++++++----
src/bot/orchestrator.ts | 63 ++++++++++++++++++++++++++++------------
2 files changed, 59 insertions(+), 24 deletions(-)
diff --git a/src/bot/authProcedure.ts b/src/bot/authProcedure.ts
index b7f6953..a895450 100644
--- a/src/bot/authProcedure.ts
+++ b/src/bot/authProcedure.ts
@@ -36,11 +36,21 @@ export class AuthProcedure {
try {
this._logger.info(`Authenticating as ${email}...`);
- // Navigate to Microsoft login
- await this._page.goto(_LOGIN_URL, {
- waitUntil: 'domcontentloaded',
- timeout: 30000,
- });
+ // Only navigate to login page if not already there.
+ // When called after clicking "Sign in" on Teams pre-join page,
+ // the browser is already on login.microsoftonline.com WITH a return URL
+ // embedded by Teams. Navigating again would destroy that return URL!
+ const currentUrl = this._page.url();
+ if (!currentUrl.includes('login.microsoftonline.com')) {
+ this._logger.info('Not on login page yet, navigating to Microsoft login...');
+ await this._page.goto(_LOGIN_URL, {
+ waitUntil: 'domcontentloaded',
+ timeout: 30000,
+ });
+ } else {
+ this._logger.info('Already on Microsoft login page - skipping navigation to preserve return URL');
+ await this._page.waitForLoadState('domcontentloaded', { timeout: 15000 });
+ }
// Wait for email input
const emailInput = await this._page.waitForSelector(
diff --git a/src/bot/orchestrator.ts b/src/bot/orchestrator.ts
index 897381b..386ce4d 100644
--- a/src/bot/orchestrator.ts
+++ b/src/bot/orchestrator.ts
@@ -214,32 +214,57 @@ export class BotOrchestrator {
if (authSuccess) {
this._logger.info('Authentication via "Sign in" link succeeded');
- // After login, Teams does a redirect chain automatically:
- // Login → M365 → Teams v2 → Pre-Join page (/v2/)
- // Do NOT navigate anywhere. Just wait for the redirect chain to complete
- // and for the "Join now" button to appear on the Teams v2 pre-join page.
- this._logger.info('Waiting for Teams to redirect to authenticated pre-join page...');
+ // After login, the redirect chain goes automatically:
+ // login.microsoftonline.com → teams.microsoft.com/v2/?meetingjoin=true#/meet/...
+ // The return URL is embedded by Teams when clicking "Sign in".
+ // We just wait for the redirect to complete.
+ this._logger.info('Waiting for redirect chain to leave login.microsoftonline.com...');
- // Wait for the #prejoin-join-button to appear (up to 30 seconds)
- // This button only exists on the Teams v2 authenticated pre-join page
+ // Step 1: Wait for URL to leave login.microsoftonline.com (poll up to 45s)
+ const maxWaitMs = 45000;
+ const pollIntervalMs = 1000;
+ const startTime = Date.now();
+ let leftLoginPage = false;
+
+ while (Date.now() - startTime < maxWaitMs) {
+ const currentUrl = this._page!.url();
+ if (!currentUrl.includes('login.microsoftonline.com')) {
+ this._logger.info(`Left login page after ${Date.now() - startTime}ms. URL: ${currentUrl.substring(0, 100)}`);
+ leftLoginPage = true;
+ break;
+ }
+ await this._page!.waitForTimeout(pollIntervalMs);
+ }
+
+ if (!leftLoginPage) {
+ const stuckUrl = this._page!.url();
+ this._logger.warn(`Still on login page after ${maxWaitMs}ms. URL: ${stuckUrl.substring(0, 100)}`);
+ // Try navigating to meeting URL as fallback (auth cookies should be set)
+ this._logger.info('Fallback: navigating to meeting URL with auth cookies...');
+ await this._page!.goto(this._meetingUrl, { waitUntil: 'domcontentloaded', timeout: 30000 });
+ await this._joinProcedure!.handleLauncherIfPresent();
+ }
+
+ // Step 2: Wait for the pre-join page to fully load
+ // The #prejoin-join-button appears on the Teams v2 authenticated pre-join page
const joinButtonSelector = '#prejoin-join-button, button[data-tid="prejoin-join-button"]';
try {
await this._page!.waitForSelector(joinButtonSelector, { timeout: 30000, state: 'visible' });
- const currentUrl = this._page!.url();
- this._logger.info(`Arrived at authenticated pre-join page: ${currentUrl.substring(0, 80)}`);
+ const finalUrl = this._page!.url();
+ this._logger.info(`On authenticated pre-join page: ${finalUrl.substring(0, 100)}`);
} catch {
- const currentUrl = this._page!.url();
+ const finalUrl = this._page!.url();
const pageContent = await this._page!.evaluate(() => document.body?.innerText?.substring(0, 300) || '');
- this._logger.warn(`Join button not found after auth redirect. URL: ${currentUrl.substring(0, 80)}`);
+ this._logger.warn(`Join button not found. URL: ${finalUrl.substring(0, 100)}`);
this._logger.warn(`Page content: ${pageContent.substring(0, 200)}`);
- }
-
- // Verify we're on the authenticated pre-join page
- const pageText = await this._page!.evaluate(() => document.body?.innerText?.substring(0, 500) || '');
- if (pageText.includes('Join now')) {
- this._logger.info('On authenticated pre-join page with "Join now" button');
- } else {
- this._logger.warn(`Post-auth page content: ${pageText.substring(0, 300)}`);
+
+ // If we ended up somewhere unexpected, try meeting URL as last resort
+ if (!finalUrl.includes('teams.microsoft.com')) {
+ this._logger.info('Not on Teams - navigating to meeting URL as last resort...');
+ await this._page!.goto(this._meetingUrl, { waitUntil: 'domcontentloaded', timeout: 30000 });
+ await this._joinProcedure!.handleLauncherIfPresent();
+ await this._page!.waitForTimeout(5000);
+ }
}
} else {
this._logger.warn('Authentication via "Sign in" failed - continuing as anonymous');