diff --git a/Plugins/RSBot.General/Components/RuSroAuthService.cs b/Plugins/RSBot.General/Components/RuSroAuthService.cs index 71ea221c..45cc7eac 100644 --- a/Plugins/RSBot.General/Components/RuSroAuthService.cs +++ b/Plugins/RSBot.General/Components/RuSroAuthService.cs @@ -32,6 +32,10 @@ internal static class RuSroAuthService private const string AuthorizeEndpoint = "https://webbff.ru.4game.ru/oauth/authorize"; private const string TokenEndpoint = "https://launcherbff.ru.4game.com/connect/token"; + internal const string BrowserUserAgent = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36"; + private const string BrowserAcceptLanguage = "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7"; + private const string HardwareIdConfigKey = "RSBot.RuSro.hwid"; private const string LauncherIdConfigKey = "RSBot.RuSro.launcherid"; private const string AccessTokenConfigKey = "RSBot.RuSro.accessToken"; @@ -43,8 +47,7 @@ internal static class RuSroAuthService private const string EmailRequestGroupIdConfigKey = "RSBot.RuSro.emailRequestGroupId"; private const string EmailCodeRequestedAtConfigKey = "RSBot.RuSro.emailCodeRequestedAt"; private const int EmailCodeResendDelaySeconds = 30; - private const int MaxCaptchaAttempts = 2; - + private const int MaxAuthPageRedirects = 10; private const string Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; public static async Task Auth() @@ -265,8 +268,9 @@ private static HttpClient CreateAuthClient(string trackingId, out CookieContaine handler.Proxy = proxyConfig.CreateWebProxy(); var authClient = new HttpClient(handler); + authClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", BrowserUserAgent); authClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "application/json, text/plain, */*"); - authClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", "ru"); + authClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", BrowserAcceptLanguage); return authClient; } @@ -285,32 +289,36 @@ string state + $"&back={Uri.EscapeDataString(BackUri)}"; var requestUri = new Uri(url); - for (int captchaAttempt = 0; ; captchaAttempt++) + Uri currentUri = requestUri; + Uri captchaUri; + for (int redirectCount = 0; ; redirectCount++) { - Uri captchaUri; - using (HttpResponseMessage response = await authClient.GetAsync(requestUri)) - { - if (response.IsSuccessStatusCode) - return true; + using HttpResponseMessage response = await authClient.GetAsync(currentUri); - if (!TryGetCaptchaRedirect(response, requestUri, out captchaUri)) - { - string content = await response.Content.ReadAsStringAsync(); - throw CreateRequestException("Opening the 4game authorization page", response.StatusCode, content); - } + if (response.IsSuccessStatusCode) + return true; + + if (TryGetCaptchaRedirect(response, currentUri, out captchaUri)) + break; + + if (!TryGetRedirectUri(response, currentUri, out Uri redirectUri) || !IsSameOrigin(requestUri, redirectUri)) + { + string content = await response.Content.ReadAsStringAsync(); + throw CreateRequestException("Opening the 4game authorization page", response.StatusCode, content); } - if (captchaAttempt >= MaxCaptchaAttempts) + if (redirectCount >= MaxAuthPageRedirects) { throw new InvalidOperationException( - $"4game requested verification more than {MaxCaptchaAttempts} times." + $"Opening the 4game authorization page exceeded {MaxAuthPageRedirects} redirects." ); } - Log.Notify("4game requires additional verification. Complete it in the opened window."); - if (!await CaptchaWindow.ShowAsync(captchaUri, requestUri, cookieContainer)) - return false; + currentUri = redirectUri; } + + Log.Notify("4game requires additional verification. Complete it in the opened window."); + return await CaptchaWindow.ShowAsync(captchaUri, requestUri, cookieContainer); } private static async Task SendAuthJsonRequestAsync( @@ -639,13 +647,9 @@ private static bool IsRedirect(HttpStatusCode statusCode) private static bool TryGetCaptchaRedirect(HttpResponseMessage response, Uri requestUri, out Uri captchaUri) { - captchaUri = null; - if (!IsRedirect(response.StatusCode) || response.Headers.Location == null) + if (!TryGetRedirectUri(response, requestUri, out captchaUri)) return false; - Uri location = response.Headers.Location; - captchaUri = location.IsAbsoluteUri ? location : new Uri(requestUri, location); - bool isCaptcha = string.Equals(captchaUri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase) && string.Equals(captchaUri.Host, "id.4game.ru", StringComparison.OrdinalIgnoreCase) @@ -661,6 +665,24 @@ private static bool TryGetCaptchaRedirect(HttpResponseMessage response, Uri requ return isCaptcha; } + private static bool TryGetRedirectUri(HttpResponseMessage response, Uri requestUri, out Uri redirectUri) + { + redirectUri = null; + if (!IsRedirect(response.StatusCode) || response.Headers.Location == null) + return false; + + Uri location = response.Headers.Location; + redirectUri = location.IsAbsoluteUri ? location : new Uri(requestUri, location); + return true; + } + + private static bool IsSameOrigin(Uri expectedOrigin, Uri uri) + { + return string.Equals(expectedOrigin.Scheme, uri.Scheme, StringComparison.OrdinalIgnoreCase) + && string.Equals(expectedOrigin.Host, uri.Host, StringComparison.OrdinalIgnoreCase) + && expectedOrigin.Port == uri.Port; + } + private static string GetQueryParameter(Uri uri, string name) { foreach (string pair in uri.Query.TrimStart('?').Split('&', StringSplitOptions.RemoveEmptyEntries)) diff --git a/Plugins/RSBot.General/Views/CaptchaWindow.cs b/Plugins/RSBot.General/Views/CaptchaWindow.cs index 591c312f..ef1f93e7 100644 --- a/Plugins/RSBot.General/Views/CaptchaWindow.cs +++ b/Plugins/RSBot.General/Views/CaptchaWindow.cs @@ -95,7 +95,7 @@ private CaptchaWindow(Uri captchaUri, Uri returnUri, CookieContainer cookieConta Enabled = false, Margin = new Padding(8, 0, 0, 0), Size = new Size(110, 32), - Text = "Continue", + Text = "Check", }; _continueButton.Click += ContinueButton_Click; @@ -277,6 +277,7 @@ private static void ConfigureWebView2Loader() private void ConfigureBrowser() { + _webView.CoreWebView2.Settings.UserAgent = RuSroAuthService.BrowserUserAgent; _webView.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false; _webView.CoreWebView2.Settings.AreDevToolsEnabled = false; _webView.CoreWebView2.Settings.IsStatusBarEnabled = false; @@ -315,16 +316,25 @@ private async void WebView_NavigationCompleted(object sender, CoreWebView2Naviga if (!e.IsSuccess) { _statusLabel.Text = $"The verification page failed to load ({e.WebErrorStatus})."; + _continueButton.Enabled = true; return; } - if (IsReturnUri(_webView.Source)) + if (IsReturnUri(_webView.Source) && e.HttpStatusCode is >= 200 and <= 299) + { await CompleteAsync(); + return; + } + + _continueButton.Enabled = true; + _statusLabel.Text = "Complete the verification. Click Check if the page does not continue automatically."; } - private async void ContinueButton_Click(object sender, EventArgs e) + private void ContinueButton_Click(object sender, EventArgs e) { - await CompleteAsync(); + _continueButton.Enabled = false; + _statusLabel.Text = "Checking the verification result..."; + _webView.CoreWebView2.Navigate(_returnUri.AbsoluteUri); } private async Task CompleteAsync()