diff --git a/GlobalAssemblyInfo.cs b/GlobalAssemblyInfo.cs index c887db2..a721ee7 100644 --- a/GlobalAssemblyInfo.cs +++ b/GlobalAssemblyInfo.cs @@ -8,4 +8,3 @@ [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0.0-develop")] - diff --git a/MegaApiClient.Tests/MegaApiClientAsyncWrapper.cs b/MegaApiClient.Tests/MegaApiClientAsyncWrapper.cs index dbf4acc..1599506 100644 --- a/MegaApiClient.Tests/MegaApiClientAsyncWrapper.cs +++ b/MegaApiClient.Tests/MegaApiClientAsyncWrapper.cs @@ -33,6 +33,11 @@ public MegaApiClient.LogonSessionToken Login(string email, string password) return this.UnwrapException(() => this.client.LoginAsync(email, password).Result); } + public MegaApiClient.LogonSessionToken Login(string email, string password, string mfaKey) + { + return this.UnwrapException(() => this.client.LoginAsync(email, password, mfaKey).Result); + } + public MegaApiClient.LogonSessionToken Login(MegaApiClient.AuthInfos authInfos) { return this.UnwrapException(() => this.client.LoginAsync(authInfos).Result); @@ -159,11 +164,21 @@ public MegaApiClient.AuthInfos GenerateAuthInfos(string email, string password) return this.UnwrapException(() => this.client.GenerateAuthInfosAsync(email, password).Result); } + public MegaApiClient.AuthInfos GenerateAuthInfos(string email, string password, string mfaKey) + { + return this.UnwrapException(() => this.client.GenerateAuthInfosAsync(email, password, mfaKey).Result); + } + public Task LoginAsync(string email, string password) { return this.client.LoginAsync(email, password); } + public Task LoginAsync(string email, string password, string mfaKey) + { + return this.client.LoginAsync(email, password, mfaKey); + } + public Task LoginAsync(MegaApiClient.AuthInfos authInfos) { return this.client.LoginAsync(authInfos); @@ -284,6 +299,11 @@ public Task> GetNodesFromLinkAsync(Uri uri) return this.client.GenerateAuthInfosAsync(email, password); } + public Task GenerateAuthInfosAsync(string email, string password, string mfaKey) + { + return this.client.GenerateAuthInfosAsync(email, password, mfaKey); + } + private T UnwrapException(Func action) { try diff --git a/MegaApiClient/Interface/IMegaApiClient.cs b/MegaApiClient/Interface/IMegaApiClient.cs index bc0120d..e63fee8 100644 --- a/MegaApiClient/Interface/IMegaApiClient.cs +++ b/MegaApiClient/Interface/IMegaApiClient.cs @@ -1,4 +1,4 @@ -namespace CG.Web.MegaApiClient +namespace CG.Web.MegaApiClient { using System; using System.Collections.Generic; @@ -13,6 +13,8 @@ public partial interface IMegaApiClient MegaApiClient.LogonSessionToken Login(string email, string password); + MegaApiClient.LogonSessionToken Login(string email, string password, string mfaKey); + MegaApiClient.LogonSessionToken Login(MegaApiClient.AuthInfos authInfos); void Login(MegaApiClient.LogonSessionToken logonSessionToken); @@ -59,6 +61,6 @@ public partial interface IMegaApiClient INode Rename(INode node, string newName); - MegaApiClient.AuthInfos GenerateAuthInfos(string email, string password); + MegaApiClient.AuthInfos GenerateAuthInfos(string email, string password, string mfaKey = null); } -} \ No newline at end of file +} diff --git a/MegaApiClient/Interface/IMegaApiClientAsync.cs b/MegaApiClient/Interface/IMegaApiClientAsync.cs index f097406..60ee04d 100644 --- a/MegaApiClient/Interface/IMegaApiClientAsync.cs +++ b/MegaApiClient/Interface/IMegaApiClientAsync.cs @@ -9,7 +9,7 @@ namespace CG.Web.MegaApiClient public partial interface IMegaApiClient { - Task LoginAsync(string email, string password); + Task LoginAsync(string email, string password, string mfaKey = null); Task LoginAsync(MegaApiClient.AuthInfos authInfos); @@ -58,6 +58,8 @@ public partial interface IMegaApiClient Task> GetNodesFromLinkAsync(Uri uri); Task GenerateAuthInfosAsync(string email, string password); + + Task GenerateAuthInfosAsync(string email, string password, string mfaKey); } } #endif diff --git a/MegaApiClient/MegaApiClient.cs b/MegaApiClient/MegaApiClient.cs index 1b728fe..1166d5a 100644 --- a/MegaApiClient/MegaApiClient.cs +++ b/MegaApiClient/MegaApiClient.cs @@ -88,9 +88,10 @@ public MegaApiClient(Options options, IWebClient webClient) /// /// email /// password + /// /// object containing encrypted data /// email or password is null - public AuthInfos GenerateAuthInfos(string email, string password) + public AuthInfos GenerateAuthInfos(string email, string password, string mfaKey = null) { if (string.IsNullOrEmpty(email)) { @@ -121,6 +122,14 @@ public AuthInfos GenerateAuthInfos(string email, string password) } // Derived key contains master key (0-16) and password hash (16-32) + if(!string.IsNullOrEmpty(mfaKey)) + { + return new AuthInfos( + email, + derivedKeyBytes.Skip(16).ToArray().ToBase64(), + derivedKeyBytes.Take(16).ToArray(), + mfaKey); + } return new AuthInfos( email, derivedKeyBytes.Skip(16).ToArray().ToBase64(), @@ -136,7 +145,10 @@ public AuthInfos GenerateAuthInfos(string email, string password) // Hash email and password to decrypt master key on Mega servers string hash = GenerateHash(email.ToLowerInvariant(), passwordAesKey); - + if (!string.IsNullOrEmpty(mfaKey)) + { + return new AuthInfos(email, hash, passwordAesKey, mfaKey); + } return new AuthInfos(email, hash, passwordAesKey); } else @@ -165,6 +177,20 @@ public LogonSessionToken Login(string email, string password) return this.Login(GenerateAuthInfos(email, password)); } + /// + /// Login to Mega.co.nz service using email/password credentials + /// + /// email + /// password + /// + /// Service is not available or credentials are invalid + /// email or password is null + /// Already logged in + public LogonSessionToken Login(string email, string password, string mfaKey) + { + return this.Login(GenerateAuthInfos(email, password, mfaKey)); + } + /// /// Login to Mega.co.nz service using hashed credentials /// @@ -183,7 +209,15 @@ public LogonSessionToken Login(AuthInfos authInfos) this.authenticatedLogin = true; // Request Mega Api - LoginRequest request = new LoginRequest(authInfos.Email, authInfos.Hash); + LoginRequest request; + if (!string.IsNullOrEmpty(authInfos.MFAKey)) + { + request = new LoginRequest(authInfos.Email, authInfos.Hash, authInfos.MFAKey); + } + else + { + request = new LoginRequest(authInfos.Email, authInfos.Hash); + } LoginResponse response = this.Request(request); // Decrypt master key using our password key @@ -1214,6 +1248,14 @@ public AuthInfos(string email, string hash, byte[] passwordAesKey) this.PasswordAesKey = passwordAesKey; } + public AuthInfos(string email, string hash, byte[] passwordAesKey, string mfaKey) + { + this.Email = email; + this.Hash = hash; + this.PasswordAesKey = passwordAesKey; + this.MFAKey = mfaKey; + } + [JsonProperty] public string Email { get; private set; } @@ -1222,6 +1264,9 @@ public AuthInfos(string email, string hash, byte[] passwordAesKey) [JsonProperty] public byte[] PasswordAesKey { get; private set; } + + [JsonProperty] + public string MFAKey { get; private set; } } public class LogonSessionToken : IEquatable diff --git a/MegaApiClient/MegaApiClientAsync.cs b/MegaApiClient/MegaApiClientAsync.cs index 76f0d5c..f39a661 100644 --- a/MegaApiClient/MegaApiClientAsync.cs +++ b/MegaApiClient/MegaApiClientAsync.cs @@ -9,11 +9,11 @@ namespace CG.Web.MegaApiClient public partial class MegaApiClient : IMegaApiClient { - #region Public async methods +#region Public async methods - public Task LoginAsync(string email, string password) + public Task LoginAsync(string email, string password, string mfaKey = null) { - return Task.Run(() => this.Login(email, password)); + return Task.Run(() => this.Login(email, password, mfaKey)); } public Task LoginAsync(AuthInfos authInfos) @@ -177,7 +177,12 @@ public Task> GetNodesFromLinkAsync(Uri uri) return Task.Run(() => this.GenerateAuthInfos(email, password)); } - #endregion + public Task GenerateAuthInfosAsync(string email, string password, string mfaKey) + { + return Task.Run(() => this.GenerateAuthInfos(email, password, mfaKey)); + } + +#endregion } } #endif diff --git a/MegaApiClient/Serialization/Login.cs b/MegaApiClient/Serialization/Login.cs index 8f0f9a7..c8a3921 100644 --- a/MegaApiClient/Serialization/Login.cs +++ b/MegaApiClient/Serialization/Login.cs @@ -11,11 +11,22 @@ public LoginRequest(string userHandle, string passwordHash) this.PasswordHash = passwordHash; } + public LoginRequest(string userHandle, string passwordHash, string mfaKey) + : base("us") + { + this.UserHandle = userHandle; + this.PasswordHash = passwordHash; + this.MFAKey = mfaKey; + } + [JsonProperty("user")] public string UserHandle { get; private set; } [JsonProperty("uh")] public string PasswordHash { get; private set; } + + [JsonProperty("mfa")] + public string MFAKey { get; private set; } } internal class LoginResponse