mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2025-12-15 22:10:30 +00:00
Fix authentication flow when reaching max failures in credentials provider
This one is tricky, previously we've properly handled max failures, and told SK2 to abort the request with operation canceled exception, which we even handled back in bot flow, so it looked OK at first, but the bot didn't do anything with that, which resulted in forced TryAnotherCM disconnection after 1 minute of inactivity. Since we need to be informed what to do in such case, simple cancellation of flow is not enough, we require a custom exception to handle, which will tell us the precise reason for failure + possible count of them, and that will in result allow us to react accordingly in the bot flow, e.g. by stopping the bot if needed.
This commit is contained in:
@@ -2778,14 +2778,14 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
|
||||
InitConnectionFailureTimer();
|
||||
|
||||
if (string.IsNullOrEmpty(RefreshToken)) {
|
||||
BotCredentialsProvider botCredentialsProvider = new(this);
|
||||
|
||||
AuthPollResult pollResult;
|
||||
|
||||
try {
|
||||
using CancellationTokenSource authCancellationTokenSource = new();
|
||||
|
||||
CredentialsAuthSession authSession = await SteamClient.Authentication.BeginAuthSessionViaCredentialsAsync(
|
||||
new AuthSessionDetails {
|
||||
Authenticator = new BotCredentialsProvider(this, authCancellationTokenSource),
|
||||
Authenticator = botCredentialsProvider,
|
||||
DeviceFriendlyName = machineName,
|
||||
GuardData = BotConfig.UseLoginKeys ? BotDatabase.SteamGuardData : null,
|
||||
IsPersistentSession = true,
|
||||
@@ -2794,10 +2794,12 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
|
||||
}
|
||||
).ConfigureAwait(false);
|
||||
|
||||
pollResult = await authSession.PollingWaitForResultAsync(authCancellationTokenSource.Token).ConfigureAwait(false);
|
||||
pollResult = await authSession.PollingWaitForResultAsync().ConfigureAwait(false);
|
||||
} catch (AsyncJobFailedException e) {
|
||||
ArchiLogger.LogGenericWarningException(e);
|
||||
|
||||
LoginFailures += botCredentialsProvider.LoginFailures;
|
||||
|
||||
await HandleLoginResult(EResult.Timeout, EResult.Timeout).ConfigureAwait(false);
|
||||
|
||||
ReconnectOnUserInitiated = true;
|
||||
@@ -2807,6 +2809,17 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
|
||||
} catch (AuthenticationException e) {
|
||||
ArchiLogger.LogGenericWarningException(e);
|
||||
|
||||
LoginFailures += botCredentialsProvider.LoginFailures;
|
||||
|
||||
await HandleLoginResult(e.Result, e.Result).ConfigureAwait(false);
|
||||
|
||||
ReconnectOnUserInitiated = true;
|
||||
SteamClient.Disconnect();
|
||||
|
||||
return;
|
||||
} catch (BotAuthenticationException e) {
|
||||
LoginFailures += botCredentialsProvider.LoginFailures;
|
||||
|
||||
await HandleLoginResult(e.Result, e.Result).ConfigureAwait(false);
|
||||
|
||||
ReconnectOnUserInitiated = true;
|
||||
@@ -2814,10 +2827,18 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
|
||||
|
||||
return;
|
||||
} catch (OperationCanceledException) {
|
||||
// This is okay, we already took care of that and can ignore it here
|
||||
LoginFailures += botCredentialsProvider.LoginFailures;
|
||||
|
||||
await HandleLoginResult(EResult.Timeout, EResult.Timeout).ConfigureAwait(false);
|
||||
|
||||
ReconnectOnUserInitiated = true;
|
||||
SteamClient.Disconnect();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
LoginFailures += botCredentialsProvider.LoginFailures;
|
||||
|
||||
if (!string.IsNullOrEmpty(pollResult.NewGuardData) && BotConfig.UseLoginKeys) {
|
||||
BotDatabase.SteamGuardData = pollResult.NewGuardData;
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.Core;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using ArchiSteamFarm.Steam.Security;
|
||||
using ArchiSteamFarm.Storage;
|
||||
using SteamKit2;
|
||||
using SteamKit2.Authentication;
|
||||
@@ -34,19 +34,16 @@ using SteamKit2.Authentication;
|
||||
namespace ArchiSteamFarm.Steam.Integration;
|
||||
|
||||
internal sealed class BotCredentialsProvider : IAuthenticator {
|
||||
private const byte MaxLoginFailures = 5;
|
||||
private const byte MaxLoginFailures = 3;
|
||||
|
||||
private readonly Bot Bot;
|
||||
private readonly CancellationTokenSource CancellationTokenSource;
|
||||
|
||||
private byte LoginFailures;
|
||||
internal byte LoginFailures { get; private set; }
|
||||
|
||||
internal BotCredentialsProvider(Bot bot, CancellationTokenSource cancellationTokenSource) {
|
||||
internal BotCredentialsProvider(Bot bot) {
|
||||
ArgumentNullException.ThrowIfNull(bot);
|
||||
ArgumentNullException.ThrowIfNull(cancellationTokenSource);
|
||||
|
||||
Bot = bot;
|
||||
CancellationTokenSource = cancellationTokenSource;
|
||||
}
|
||||
|
||||
public async Task<bool> AcceptDeviceConfirmationAsync() {
|
||||
@@ -75,24 +72,26 @@ internal sealed class BotCredentialsProvider : IAuthenticator {
|
||||
throw new InvalidEnumArgumentException(nameof(inputType), (int) inputType, typeof(ASF.EUserInputType));
|
||||
}
|
||||
|
||||
if (previousCodeWasIncorrect && (++LoginFailures >= MaxLoginFailures)) {
|
||||
EResult reason = inputType == ASF.EUserInputType.TwoFactorAuthentication ? EResult.TwoFactorCodeMismatch : EResult.InvalidLoginAuthCode;
|
||||
EResult reason = inputType == ASF.EUserInputType.TwoFactorAuthentication ? EResult.TwoFactorCodeMismatch : EResult.InvalidLoginAuthCode;
|
||||
|
||||
if (previousCodeWasIncorrect) {
|
||||
Bot.ArchiLogger.LogGenericWarning(Strings.FormatBotUnableToLogin(reason, reason));
|
||||
|
||||
await CancellationTokenSource.CancelAsync().ConfigureAwait(false);
|
||||
|
||||
return "";
|
||||
if (++LoginFailures >= MaxLoginFailures) {
|
||||
throw new BotAuthenticationException(reason);
|
||||
}
|
||||
}
|
||||
|
||||
string? result = await Bot.RequestInput(inputType, previousCodeWasIncorrect).ConfigureAwait(false);
|
||||
string? input = await Bot.RequestInput(inputType, previousCodeWasIncorrect).ConfigureAwait(false);
|
||||
|
||||
if (string.IsNullOrEmpty(result)) {
|
||||
await CancellationTokenSource.CancelAsync().ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(input)) {
|
||||
Bot.ArchiLogger.LogGenericWarning(Strings.FormatErrorIsEmpty(nameof(input)));
|
||||
|
||||
return "";
|
||||
LoginFailures = MaxLoginFailures;
|
||||
|
||||
throw new BotAuthenticationException(reason);
|
||||
}
|
||||
|
||||
return result;
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
40
ArchiSteamFarm/Steam/Security/BotAuthenticationException.cs
Normal file
40
ArchiSteamFarm/Steam/Security/BotAuthenticationException.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// |
|
||||
// Copyright 2015-2025 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
// |
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// |
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// |
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using SteamKit2;
|
||||
|
||||
namespace ArchiSteamFarm.Steam.Security;
|
||||
|
||||
internal sealed class BotAuthenticationException : Exception {
|
||||
internal readonly EResult Result;
|
||||
|
||||
internal BotAuthenticationException(EResult result) {
|
||||
if (!Enum.IsDefined(result)) {
|
||||
throw new InvalidEnumArgumentException(nameof(result), (int) result, typeof(EResult));
|
||||
}
|
||||
|
||||
Result = result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user