From 4e8bd59ff47bded51aec9fa2af30fc226fe74764 Mon Sep 17 00:00:00 2001 From: LightZirconite Date: Tue, 4 Nov 2025 23:09:31 +0100 Subject: [PATCH] feat: Enhance login state detection with strict checks for portal elements and improved handling of unknown states --- src/functions/Login.ts | 17 ++++++++++++++--- src/util/LoginStateDetector.ts | 28 +++++++++++++++++++++------- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/functions/Login.ts b/src/functions/Login.ts index 3a9c61c..34fde85 100644 --- a/src/functions/Login.ts +++ b/src/functions/Login.ts @@ -461,13 +461,24 @@ export class Login { ) if (passwordPageReached === LoginState.LoggedIn) { - this.bot.log(this.bot.isMobile, 'LOGIN', 'Already authenticated after email (fast path)') - return + // Double-check: verify we're actually on rewards portal with activities + const actuallyLoggedIn = await page.locator('#more-activities, html[data-role-name*="RewardsPortal"]') + .first() + .isVisible({ timeout: 2000 }) + .catch(() => false) + + if (actuallyLoggedIn) { + this.bot.log(this.bot.isMobile, 'LOGIN', 'Already authenticated after email (fast path)') + return + } else { + this.bot.log(this.bot.isMobile, 'LOGIN', 'False positive on LoggedIn state, continuing with password entry', 'warn') + // Continue to password entry + } } if (!passwordPageReached) { this.bot.log(this.bot.isMobile, 'LOGIN', 'Password page not reached after 8s, continuing anyway...', 'warn') - } else { + } else if (passwordPageReached !== LoginState.LoggedIn) { this.bot.log(this.bot.isMobile, 'LOGIN', `Transitioned to state: ${passwordPageReached}`) } diff --git a/src/util/LoginStateDetector.ts b/src/util/LoginStateDetector.ts index 534f695..6c4528f 100644 --- a/src/util/LoginStateDetector.ts +++ b/src/util/LoginStateDetector.ts @@ -43,9 +43,10 @@ export class LoginStateDetector { // State 1: Already logged in (rewards portal) if (url.includes('rewards.bing.com') || url.includes('rewards.microsoft.com')) { - const hasPortal = await page.locator('html[data-role-name*="RewardsPortal"], #dashboard, main[data-bi-name="dashboard"]') + // STRICT CHECK: Must have actual portal elements, not just domain + const hasPortal = await page.locator('html[data-role-name*="RewardsPortal"], #dashboard, main[data-bi-name="dashboard"], #more-activities') .first() - .isVisible({ timeout: 1000 }) + .isVisible({ timeout: 2000 }) .catch(() => false) if (hasPortal) { @@ -53,12 +54,25 @@ export class LoginStateDetector { return { state: LoginState.LoggedIn, confidence: 'high', url, indicators } } - // On rewards domain but no portal = might be loading or error - const bodyLength = await page.evaluate(() => document.body?.innerText?.length || 0).catch(() => 0) - if (bodyLength > 100) { - indicators.push('On rewards domain, checking content...') - return { state: LoginState.LoggedIn, confidence: 'medium', url, indicators } + // On rewards domain but no portal = still loading or redirecting to login + // Do NOT assume logged in just because of domain + indicators.push('On rewards domain but portal not detected') + + // Check if actually redirecting to login + const isRedirectingToLogin = url.includes('login.live.com') || + url.includes('login.microsoftonline.com') || + await page.locator('input[type="email"], input[name="loginfmt"]') + .first() + .isVisible({ timeout: 500 }) + .catch(() => false) + + if (isRedirectingToLogin) { + indicators.push('Redirecting to login') + return { state: LoginState.EmailPage, confidence: 'medium', url, indicators } } + + // Unknown state - not logged in, not login page + return { state: LoginState.Unknown, confidence: 'low', url, indicators } } // State 2: Microsoft login pages