From 31e884f57b914b2227f8c69f80692c3cb75f292d Mon Sep 17 00:00:00 2001 From: Jusmejtr <55502113+Jusmejtr@users.noreply.github.com> Date: Thu, 24 Jul 2025 16:56:40 +0200 Subject: [PATCH 1/2] add support for mobile notification --- Core/LoginWindowState.cs | 1 + Core/WindowUtils.cs | 49 ++++++++++++ Views/AccountsWindow.xaml.cs | 142 +++++++++++++++++++++++++++++++++-- 3 files changed, 187 insertions(+), 5 deletions(-) diff --git a/Core/LoginWindowState.cs b/Core/LoginWindowState.cs index c5e96c3..f486be5 100644 --- a/Core/LoginWindowState.cs +++ b/Core/LoginWindowState.cs @@ -8,6 +8,7 @@ enum LoginWindowState Selection, Login, Code, + MobileConfirmation, Loading, Success } diff --git a/Core/WindowUtils.cs b/Core/WindowUtils.cs index c5fcc1f..8fb8404 100644 --- a/Core/WindowUtils.cs +++ b/Core/WindowUtils.cs @@ -293,6 +293,10 @@ public static LoginWindowState GetLoginWindowState(WindowHandle loginWindow) { return LoginWindowState.Code; } + else if (inputs.Count == 0 && buttons.Count == 0 && groups.Count == 0 && images.Count == 3 && texts.Count == 7) + { + return LoginWindowState.MobileConfirmation; + } else if (inputs.Count == 2 && buttons.Count == 1) { return LoginWindowState.Login; @@ -334,6 +338,51 @@ public static LoginWindowState HandleAccountSelection(WindowHandle loginWindow) return LoginWindowState.Invalid; } + public static LoginWindowState TryMobileToCodeSwitch(WindowHandle loginWindow) + { + using (var automation = new UIA3Automation()) + { + try + { + AutomationElement window = automation.FromHandle(loginWindow.RawPtr); + + window.Focus(); + + AutomationElement document = window.FindFirstDescendant(e => e.ByControlType(ControlType.Document)); + AutomationElement[] children = document.FindAllChildren(); + + var texts = new List(); + + foreach (AutomationElement element in children) + { + switch (element.ControlType) + { + case ControlType.Text: + texts.Add(element); + break; + } + } + + // Look for "Enter a code instead" text to click + foreach (var text in texts) + { + if (text.Name.ToLower().Contains("enter a code instead")) + { + text.AsButton().Invoke(); + return LoginWindowState.Code; + } + } + + } + catch (Exception e) + { + Console.WriteLine("Error switching from mobile confirmation to code entry: " + e.Message); + } + } + + return LoginWindowState.Invalid; + } + public static LoginWindowState TryCredentialsEntry(WindowHandle loginWindow, string username, string password, bool remember) { using (var automation = new UIA3Automation()) diff --git a/Views/AccountsWindow.xaml.cs b/Views/AccountsWindow.xaml.cs index f971711..b1d89c6 100644 --- a/Views/AccountsWindow.xaml.cs +++ b/Views/AccountsWindow.xaml.cs @@ -1405,7 +1405,7 @@ private void EnterCredentials(Process steamProcess, Account account, int tryCoun SetWindowTitle("Working"); LoginWindowState state = LoginWindowState.None; - while (state != LoginWindowState.Success && state != LoginWindowState.Code) + while (state != LoginWindowState.Success && state != LoginWindowState.Code && state != LoginWindowState.MobileConfirmation) { if (steamProcess.HasExited || state == LoginWindowState.Error) { @@ -1415,7 +1415,6 @@ private void EnterCredentials(Process steamProcess, Account account, int tryCoun Thread.Sleep(100); state = WindowUtils.GetLoginWindowState(steamLoginWindow); - if (state == LoginWindowState.Selection) { WindowUtils.HandleAccountSelection(steamLoginWindow); @@ -1428,13 +1427,137 @@ private void EnterCredentials(Process steamProcess, Account account, int tryCoun state = WindowUtils.TryCredentialsEntry(steamLoginWindow, account.Name, password, settings.User.RememberPassword); } } - + + // Check what type of authentication is required after credential entry + Thread.Sleep(3000); // Small delay to let the UI update + state = WindowUtils.GetLoginWindowState(steamLoginWindow); string secret = StringCipher.Decrypt(account.SharedSecret, eKey); - if (secret != null && secret.Length > 0) + // Handle both Code and MobileConfirmation states + if ((state == LoginWindowState.Code || state == LoginWindowState.MobileConfirmation) && secret != null && secret.Length > 0) + { + // If it's mobile confirmation, try to switch to code entry first + if (state == LoginWindowState.MobileConfirmation) + { + Console.WriteLine("Mobile confirmation detected and shared secret available. Switching to 2FA code entry..."); + SetWindowTitle("Switching to 2FA code..."); + + // Try to switch from mobile confirmation to code entry + LoginWindowState switchResult = WindowUtils.TryMobileToCodeSwitch(steamLoginWindow); + Thread.Sleep(500); + if (switchResult == LoginWindowState.Code) + { + Console.WriteLine("Successfully switched to 2FA code entry mode"); + state = LoginWindowState.Code; + } + else + { + Console.WriteLine("Failed to switch to code entry, falling back to manual mobile confirmation"); + MessageBox.Show("Could not automatically switch to 2FA code entry. Please manually select 'Use code instead' or approve on your mobile device.", + "Info", MessageBoxButton.OK, MessageBoxImage.Information); + + // Fall back to mobile confirmation waiting + ShowMobileConfirmationStatus("Please check your mobile device"); + + // Wait for mobile confirmation or timeout + int waitCounter = 0; + int maxWaitTime = 120000; // 2 minutes in milliseconds + int statusUpdateInterval = 15000; // Update status every 15 seconds + int lastStatusUpdate = 0; + + while (state == LoginWindowState.MobileConfirmation && waitCounter < maxWaitTime) + { + if (steamProcess.HasExited) + { + return; + } + + Thread.Sleep(1000); + waitCounter += 1000; + + // Update status periodically + if (waitCounter - lastStatusUpdate >= statusUpdateInterval) + { + int remainingSeconds = (maxWaitTime - waitCounter) / 1000; + lastStatusUpdate = waitCounter; + } + + state = WindowUtils.GetLoginWindowState(steamLoginWindow); + + // Check if Steam client window is now available (login successful) + if (WindowUtils.GetMainSteamClientWindow(steamProcess).IsValid) + { + PostLogin(); + return; + } + } + + if (waitCounter >= maxWaitTime) + { + MessageBox.Show("Mobile confirmation timed out. Please try again.", "Timeout", MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } + + PostLogin(); + return; + } + } + + // Now handle 2FA code entry (either original Code state or switched from Mobile) + if (state == LoginWindowState.Code) + { + Console.WriteLine("Entering 2FA code..."); + EnterReact2FA(steamProcess, account, tryCount); + } + } + // Handle mobile confirmation when no shared secret is available + else if (state == LoginWindowState.MobileConfirmation) { - EnterReact2FA(steamProcess, account, tryCount); + Console.WriteLine("Mobile confirmation detected, but no shared secret configured. Please approve on mobile device."); + ShowMobileConfirmationStatus("Please check your mobile device"); + + // Wait for mobile confirmation or timeout + int waitCounter = 0; + int maxWaitTime = 120000; // 2 minutes in milliseconds + int statusUpdateInterval = 15000; // Update status every 15 seconds + int lastStatusUpdate = 0; + + while (state == LoginWindowState.MobileConfirmation && waitCounter < maxWaitTime) + { + if (steamProcess.HasExited) + { + return; + } + + Thread.Sleep(1000); + waitCounter += 1000; + + // Update status periodically + if (waitCounter - lastStatusUpdate >= statusUpdateInterval) + { + int remainingSeconds = (maxWaitTime - waitCounter) / 1000; + lastStatusUpdate = waitCounter; + } + + state = WindowUtils.GetLoginWindowState(steamLoginWindow); + + // Check if Steam client window is now available (login successful) + if (WindowUtils.GetMainSteamClientWindow(steamProcess).IsValid) + { + PostLogin(); + return; + } + } + + if (waitCounter >= maxWaitTime) + { + MessageBox.Show("Mobile confirmation timed out. Please try again.", "Timeout", MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } + + PostLogin(); } + // Handle case where no 2FA is configured or login completed else { Thread.Sleep(settings.User.SleepTime); @@ -1555,6 +1678,15 @@ private void PostLogin() } } + private void ShowMobileConfirmationStatus(string message) + { + Console.WriteLine(message); + // You could also update a status label or show a non-blocking notification here + Dispatcher.Invoke(() => { + SetWindowTitle("Mobile Confirmation - " + message); + }); + } + private void SortAccounts(SortType sortType) { if (accounts.Count > 0) From bc80106ac474e71dd158d532e791f2307682e1c0 Mon Sep 17 00:00:00 2001 From: Jusmejtr <55502113+Jusmejtr@users.noreply.github.com> Date: Thu, 24 Jul 2025 17:05:37 +0200 Subject: [PATCH 2/2] remove some messages --- Views/AccountsWindow.xaml.cs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/Views/AccountsWindow.xaml.cs b/Views/AccountsWindow.xaml.cs index b1d89c6..bc041b7 100644 --- a/Views/AccountsWindow.xaml.cs +++ b/Views/AccountsWindow.xaml.cs @@ -1440,7 +1440,6 @@ private void EnterCredentials(Process steamProcess, Account account, int tryCoun if (state == LoginWindowState.MobileConfirmation) { Console.WriteLine("Mobile confirmation detected and shared secret available. Switching to 2FA code entry..."); - SetWindowTitle("Switching to 2FA code..."); // Try to switch from mobile confirmation to code entry LoginWindowState switchResult = WindowUtils.TryMobileToCodeSwitch(steamLoginWindow); @@ -1455,10 +1454,7 @@ private void EnterCredentials(Process steamProcess, Account account, int tryCoun Console.WriteLine("Failed to switch to code entry, falling back to manual mobile confirmation"); MessageBox.Show("Could not automatically switch to 2FA code entry. Please manually select 'Use code instead' or approve on your mobile device.", "Info", MessageBoxButton.OK, MessageBoxImage.Information); - - // Fall back to mobile confirmation waiting - ShowMobileConfirmationStatus("Please check your mobile device"); - + // Wait for mobile confirmation or timeout int waitCounter = 0; int maxWaitTime = 120000; // 2 minutes in milliseconds @@ -1514,7 +1510,6 @@ private void EnterCredentials(Process steamProcess, Account account, int tryCoun else if (state == LoginWindowState.MobileConfirmation) { Console.WriteLine("Mobile confirmation detected, but no shared secret configured. Please approve on mobile device."); - ShowMobileConfirmationStatus("Please check your mobile device"); // Wait for mobile confirmation or timeout int waitCounter = 0; @@ -1678,15 +1673,6 @@ private void PostLogin() } } - private void ShowMobileConfirmationStatus(string message) - { - Console.WriteLine(message); - // You could also update a status label or show a non-blocking notification here - Dispatcher.Invoke(() => { - SetWindowTitle("Mobile Confirmation - " + message); - }); - } - private void SortAccounts(SortType sortType) { if (accounts.Count > 0)