From 08194fe241450cc5e2d8a4e2105eeaa726181c05 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Tue, 17 Feb 2026 10:50:01 +0100
Subject: [PATCH] fix: wait for Teams login redirect + universal auth selectors
Co-authored-by: Cursor
---
src/bot/authProcedure.ts | 90 +++++++++++++++---------------------
src/bot/authTestProcedure.ts | 20 +++++---
2 files changed, 50 insertions(+), 60 deletions(-)
diff --git a/src/bot/authProcedure.ts b/src/bot/authProcedure.ts
index 0e4d29b..bab795e 100644
--- a/src/bot/authProcedure.ts
+++ b/src/bot/authProcedure.ts
@@ -121,23 +121,21 @@ export class AuthProcedure {
/**
* Wait for the email input field.
*
- * Hybrid flow: Teams inline modal has input#emailOrPhonerm [data-testid="emailInput"]
- * Direct flow: MS login portal has input[name="loginfmt"]
+ * Uses universal selectors that work on both:
+ * - MS login portal (login.microsoftonline.com): input[name="loginfmt"]
+ * - Teams inline modal (light-meetings): input#emailOrPhonerm
*/
- private async _waitForEmailInput(isInlineFlow: boolean) {
+ private async _waitForEmailInput(_isInlineFlow: boolean) {
this._logger.info('Waiting for email input field...');
- // Primary selectors — exact IDs/attributes from the real HTML
- const selectors = isInlineFlow
- ? [
- 'input#emailOrPhonerm', // Teams inline modal (exact ID)
- 'input[data-testid="emailInput"]', // Teams inline modal (data-testid)
- 'input[placeholder="Enter your email"]', // Teams inline modal (placeholder)
- ]
- : [
- 'input[name="loginfmt"]', // MS login portal
- 'input[type="email"]', // MS login portal fallback
- ];
+ // Universal selectors — match both MS login portal and Teams inline modal
+ const selectors = [
+ 'input[name="loginfmt"]', // MS login portal (primary)
+ 'input[type="email"]', // MS login portal fallback
+ 'input#emailOrPhonerm', // Teams inline modal (exact ID)
+ 'input[data-testid="emailInput"]', // Teams inline modal (data-testid)
+ 'input[placeholder="Enter your email"]', // Teams inline modal (placeholder)
+ ];
const combinedSelector = selectors.join(', ');
@@ -161,53 +159,37 @@ export class AuthProcedure {
/**
* Click the "Next" button after entering email.
*
- * Hybrid flow: button[data-testid="authLoginDialogNextButton"] — initially disabled,
- * becomes enabled after email is typed. We wait for it to be enabled.
- * Direct flow: input[type="submit"] on the MS login portal.
+ * Tries all known button selectors from both MS login portal and Teams inline modal.
+ * Works regardless of which page we're on.
*/
- private async _clickNextButton(isInlineFlow: boolean): Promise {
- if (isInlineFlow) {
- // Teams inline modal: wait for the Next button to become enabled
- const nextSelector = 'button[data-testid="authLoginDialogNextButton"]';
- this._logger.info('Waiting for Teams Next button to become enabled...');
+ private async _clickNextButton(_isInlineFlow: boolean): Promise {
+ // Try all known Next/Submit button selectors (MS login portal + Teams inline modal)
+ const selectors = [
+ 'input[type="submit"]', // MS login portal
+ 'button[type="submit"]', // MS login portal alt
+ 'button[data-testid="authLoginDialogNextButton"]', // Teams inline modal
+ ];
+
+ for (const selector of selectors) {
try {
- await this._page.waitForSelector(`${nextSelector}:not([disabled])`, {
- timeout: 10000,
+ const button = await this._page.waitForSelector(selector, {
+ timeout: 5000,
state: 'visible',
});
- await this._page.click(nextSelector);
- this._logger.info('Clicked Teams Next button');
- // After clicking Next, Teams redirects to login.microsoftonline.com
- // Wait for the redirect to begin
- await this._page.waitForTimeout(3000);
- } catch {
- this._logger.warn('Teams Next button not found or not enabled, pressing Enter');
- await this._page.keyboard.press('Enter');
- await this._page.waitForTimeout(3000);
- }
- } else {
- // MS login portal: click submit button
- const selectors = [
- 'input[type="submit"]',
- 'button[type="submit"]',
- ];
- for (const selector of selectors) {
- try {
- const button = await this._page.$(selector);
- if (button && await button.isVisible()) {
- await button.click();
- this._logger.info(`Clicked Next: ${selector}`);
- await this._page.waitForTimeout(2000);
- return;
- }
- } catch {
- // Continue
+ if (button) {
+ await button.click();
+ this._logger.info(`Clicked Next: ${selector}`);
+ await this._page.waitForTimeout(3000);
+ return;
}
+ } catch {
+ // Selector not found, try next
}
- this._logger.warn('No Next button found, pressing Enter');
- await this._page.keyboard.press('Enter');
- await this._page.waitForTimeout(2000);
}
+
+ this._logger.warn('No Next button found via selectors, pressing Enter');
+ await this._page.keyboard.press('Enter');
+ await this._page.waitForTimeout(3000);
}
/**
diff --git a/src/bot/authTestProcedure.ts b/src/bot/authTestProcedure.ts
index 40d6cab..1c58227 100644
--- a/src/bot/authTestProcedure.ts
+++ b/src/bot/authTestProcedure.ts
@@ -195,18 +195,26 @@ async function _runVariant(
waitUntil: 'domcontentloaded',
timeout: 30000,
});
- await page.waitForTimeout(3000);
- _log('info', `After Teams redirect: ${page.url().substring(0, 120)}`);
+
+ // Wait for Teams to redirect to login.microsoftonline.com (up to 30s)
+ _log('info', 'Waiting for login redirect to login.microsoftonline.com...');
+ try {
+ await page.waitForURL('**/login.microsoftonline.com/**', { timeout: 30000 });
+ _log('info', `Redirected to login: ${page.url().substring(0, 150)}`);
+ } catch {
+ _log('warn', `No login redirect after 30s, current URL: ${page.url().substring(0, 150)}`);
+ }
// Step 2: Login
if (botAccountEmail && botAccountPassword) {
authAttempted = true;
- const onLoginPage = page.url().includes('login.microsoftonline.com') || page.url().includes('login.live.com');
- _log('info', `On login page: ${onLoginPage}, authenticating as ${botAccountEmail}`);
+ _log('info', `Authenticating as ${botAccountEmail} (skipNavigation=true to preserve OAuth context)`);
const authProcedure = new AuthProcedure(page, logger);
- // skipNavigation=true because we're already on the login page after teams.microsoft.com redirect
- authSuccess = await authProcedure.authenticateWithMicrosoft(botAccountEmail, botAccountPassword, !onLoginPage);
+ // Always skipNavigation=true: we're already on login.microsoftonline.com with the correct
+ // OAuth parameters (client_id, redirect_uri for Teams). Navigating to plain
+ // login.microsoftonline.com would lose this context.
+ authSuccess = await authProcedure.authenticateWithMicrosoft(botAccountEmail, botAccountPassword, true);
_log(authSuccess ? 'info' : 'warn', `Auth result: ${authSuccess ? 'success' : 'failed'}`);
}