Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 46 additions & 24 deletions Plugins/RSBot.General/Components/RuSroAuthService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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<bool> Auth()
Expand Down Expand Up @@ -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;
}

Expand All @@ -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<string> SendAuthJsonRequestAsync(
Expand Down Expand Up @@ -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)
Expand All @@ -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))
Expand Down
18 changes: 14 additions & 4 deletions Plugins/RSBot.General/Views/CaptchaWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()
Expand Down
Loading