diff --git a/Wabbajack.Lib/Downloaders/INeedsLogin.cs b/Wabbajack.Lib/Downloaders/INeedsLogin.cs index 8ce03afe..b9aae38e 100644 --- a/Wabbajack.Lib/Downloaders/INeedsLogin.cs +++ b/Wabbajack.Lib/Downloaders/INeedsLogin.cs @@ -16,20 +16,29 @@ namespace Wabbajack.Lib.Downloaders Uri? IconUri { get; } } + public enum LoginReturnCode + { + InternalError = -1, + Success = 0, + BadInput = 1, + BadCredentials = 2, + NeedsMFA = 3, + } + public struct LoginReturnMessage { public string Message; - public bool Failure; + public LoginReturnCode ReturnCode; - public LoginReturnMessage(string message, bool failure) + public LoginReturnMessage(string message, LoginReturnCode returnCode) { Message = message; - Failure = failure; + ReturnCode = returnCode; } } public interface INeedsLoginCredentials : INeedsLogin { - LoginReturnMessage LoginWithCredentials(string username, SecureString password); + LoginReturnMessage LoginWithCredentials(string username, SecureString password, string? mfa); } } diff --git a/Wabbajack.Lib/Downloaders/MEGADownloader.cs b/Wabbajack.Lib/Downloaders/MEGADownloader.cs index cac854fe..83f172b9 100644 --- a/Wabbajack.Lib/Downloaders/MEGADownloader.cs +++ b/Wabbajack.Lib/Downloaders/MEGADownloader.cs @@ -38,34 +38,34 @@ namespace Wabbajack.Lib.Downloaders { Email = infos.Email, Hash = infos.Hash, - PasswordAesKey = infos.PasswordAesKey + PasswordAesKey = infos.PasswordAesKey, + MFAKey = infos.MFAKey }; } public MegaApiClient.AuthInfos ToAuthInfos() { - //TODO: MFAKey in the update - return new MegaApiClient.AuthInfos(Email, Hash, PasswordAesKey); + return new MegaApiClient.AuthInfos(Email, Hash, PasswordAesKey, MFAKey); } } - public LoginReturnMessage LoginWithCredentials(string username, SecureString password) + public LoginReturnMessage LoginWithCredentials(string username, SecureString password, string? mfa) { MegaApiClient.AuthInfos authInfos; try { - authInfos = MegaApiClient.GenerateAuthInfos(username, password.ToNormalString()); + authInfos = MegaApiClient.GenerateAuthInfos(username, password.ToNormalString(), mfa); } catch (ApiException e) { return e.ApiResultCode switch { ApiResultCode.BadArguments => new LoginReturnMessage($"Email or password was wrong! {e.Message}", - true), + LoginReturnCode.BadCredentials), ApiResultCode.InternalError => new LoginReturnMessage( - $"Internal error occured! Please report this to the Wabbajack Team! {e.Message}", true), - _ => new LoginReturnMessage($"Error generating authentication information! {e.Message}", true) + $"Internal error occured! Please report this to the Wabbajack Team! {e.Message}", LoginReturnCode.InternalError), + _ => new LoginReturnMessage($"Error generating authentication information! {e.Message}", LoginReturnCode.InternalError) }; } @@ -78,10 +78,14 @@ namespace Wabbajack.Lib.Downloaders return e.ApiResultCode switch { ApiResultCode.TwoFactorAuthenticationError => new LoginReturnMessage( - $"Two-Factor Authentication needs to be disabled before login! {e.Message}", true), + $"Two-Factor Authentication is enabled. Input your TFA key above and try again! {e.Message}", LoginReturnCode.NeedsMFA), ApiResultCode.InternalError => new LoginReturnMessage( - $"Internal error occured! Please report this to the Wabbajack Team! {e.Message}", true), - _ => new LoginReturnMessage($"Error during login: {e.Message}", true) + $"Internal error occured! Please report this to the Wabbajack Team! {e.Message}", LoginReturnCode.InternalError), + ApiResultCode.RequestIncomplete => new LoginReturnMessage( + $"Bad credentials! {e.Message}", LoginReturnCode.BadCredentials), + ApiResultCode.ResourceNotExists => new LoginReturnMessage( + $"Bad credentials! {e.Message}", LoginReturnCode.BadCredentials), + _ => new LoginReturnMessage($"Error during login: {e.Message}", LoginReturnCode.InternalError) }; } @@ -92,7 +96,7 @@ namespace Wabbajack.Lib.Downloaders } return new LoginReturnMessage("Logged in successfully, you can now close this window.", - !MegaApiClient.IsLoggedIn || !Utils.HaveEncryptedJson(DataName)); + !MegaApiClient.IsLoggedIn || !Utils.HaveEncryptedJson(DataName) ? LoginReturnCode.Success : LoginReturnCode.InternalError); } public MegaDownloader() @@ -133,7 +137,7 @@ namespace Wabbajack.Lib.Downloaders private static MegaApiClient MegaApiClient => DownloadDispatcher.GetInstance().MegaApiClient; - private static AsyncLock _loginLock = new AsyncLock(); + private static readonly AsyncLock _loginLock = new AsyncLock(); private static async Task MegaLogin() { using var _ = await _loginLock.WaitAsync(); diff --git a/Wabbajack.Lib/Wabbajack.Lib.csproj b/Wabbajack.Lib/Wabbajack.Lib.csproj index f13f5686..da89051e 100644 --- a/Wabbajack.Lib/Wabbajack.Lib.csproj +++ b/Wabbajack.Lib/Wabbajack.Lib.csproj @@ -26,7 +26,7 @@ 1.11.23 - 1.8.0 + 1.8.1 4.7.0 diff --git a/Wabbajack/View Models/Settings/CredentialsLoginVM.cs b/Wabbajack/View Models/Settings/CredentialsLoginVM.cs index d0d7a195..38715a39 100644 --- a/Wabbajack/View Models/Settings/CredentialsLoginVM.cs +++ b/Wabbajack/View Models/Settings/CredentialsLoginVM.cs @@ -15,12 +15,18 @@ namespace Wabbajack [Reactive] public string Username { get; set; } + [Reactive] + public string MFAKey { get; set; } + [Reactive] public LoginReturnMessage ReturnMessage { get; set; } private readonly ObservableAsPropertyHelper _loginEnabled; public bool LoginEnabled => _loginEnabled.Value; + private readonly ObservableAsPropertyHelper _mfaVisible; + public bool MFAVisible => _mfaVisible.Value; + private readonly INeedsLoginCredentials _downloader; public CredentialsLoginVM(INeedsLoginCredentials downloader) @@ -31,6 +37,10 @@ namespace Wabbajack .Select(IsValidAddress) .ToGuiProperty(this, nameof(LoginEnabled)); + + _mfaVisible = this.WhenAny(x => x.ReturnMessage) + .Select(x => x.ReturnCode == LoginReturnCode.NeedsMFA) + .ToGuiProperty(this, nameof(MFAVisible)); } public void Login(SecureString password) @@ -39,17 +49,17 @@ namespace Wabbajack { if (password == null || password.Length == 0) { - ReturnMessage = new LoginReturnMessage("You need to input a password!", true); + ReturnMessage = new LoginReturnMessage("You need to input a password!", LoginReturnCode.BadInput); return; } - ReturnMessage = _downloader.LoginWithCredentials(Username, password); + ReturnMessage = _downloader.LoginWithCredentials(Username, password, string.IsNullOrWhiteSpace(MFAKey) ? null : MFAKey); password.Clear(); } catch (Exception e) { Utils.Error(e, "Exception while trying to login"); - ReturnMessage = new LoginReturnMessage($"Unhandled exception: {e.Message}", true); + ReturnMessage = new LoginReturnMessage($"Unhandled exception: {e.Message}", LoginReturnCode.InternalError); } } diff --git a/Wabbajack/Views/Settings/CredentialsLoginView.xaml b/Wabbajack/Views/Settings/CredentialsLoginView.xaml index 528e56df..cde4c645 100644 --- a/Wabbajack/Views/Settings/CredentialsLoginView.xaml +++ b/Wabbajack/Views/Settings/CredentialsLoginView.xaml @@ -11,10 +11,11 @@ d:DesignHeight="450" d:DesignWidth="800"> - + - + + @@ -27,12 +28,15 @@ Password: - + - - + diff --git a/Wabbajack/Views/Settings/CredentialsLoginView.xaml.cs b/Wabbajack/Views/Settings/CredentialsLoginView.xaml.cs index efc1951c..f369c2bf 100644 --- a/Wabbajack/Views/Settings/CredentialsLoginView.xaml.cs +++ b/Wabbajack/Views/Settings/CredentialsLoginView.xaml.cs @@ -24,6 +24,12 @@ namespace Wabbajack .DisposeWith(disposable); this.Bind(ViewModel, x => x.LoginEnabled, x => x.LoginButton.IsEnabled) .DisposeWith(disposable); + this.Bind(ViewModel, x => x.MFAKey, x => x.MFA.Text) + .DisposeWith(disposable); + this.OneWayBind(ViewModel, x => x.MFAVisible, x => x.MFA.Visibility) + .DisposeWith(disposable); + this.OneWayBind(ViewModel, x => x.MFAVisible, x => x.MFAText.Visibility) + .DisposeWith(disposable); this.OneWayBind(ViewModel, x => x.ReturnMessage.Message, x => x.Message.Text) .DisposeWith(disposable); });