Skip to content

Commit 2efd957

Browse files
authored
Add mfa, possible fix for #140 (#144) [skip ci]
* Add optional MFA token to login requests
1 parent fe7d609 commit 2efd957

File tree

7 files changed

+96
-12
lines changed

7 files changed

+96
-12
lines changed

GlobalAssemblyInfo.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,3 @@
88
[assembly: AssemblyVersion("1.0.0.0")]
99
[assembly: AssemblyFileVersion("1.0.0.0")]
1010
[assembly: AssemblyInformationalVersion("1.0.0.0-develop")]
11-

MegaApiClient.Tests/MegaApiClientAsyncWrapper.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ public MegaApiClient.LogonSessionToken Login(string email, string password)
3333
return this.UnwrapException(() => this.client.LoginAsync(email, password).Result);
3434
}
3535

36+
public MegaApiClient.LogonSessionToken Login(string email, string password, string mfaKey)
37+
{
38+
return this.UnwrapException(() => this.client.LoginAsync(email, password, mfaKey).Result);
39+
}
40+
3641
public MegaApiClient.LogonSessionToken Login(MegaApiClient.AuthInfos authInfos)
3742
{
3843
return this.UnwrapException(() => this.client.LoginAsync(authInfos).Result);
@@ -159,11 +164,21 @@ public MegaApiClient.AuthInfos GenerateAuthInfos(string email, string password)
159164
return this.UnwrapException(() => this.client.GenerateAuthInfosAsync(email, password).Result);
160165
}
161166

167+
public MegaApiClient.AuthInfos GenerateAuthInfos(string email, string password, string mfaKey)
168+
{
169+
return this.UnwrapException(() => this.client.GenerateAuthInfosAsync(email, password, mfaKey).Result);
170+
}
171+
162172
public Task<MegaApiClient.LogonSessionToken> LoginAsync(string email, string password)
163173
{
164174
return this.client.LoginAsync(email, password);
165175
}
166176

177+
public Task<MegaApiClient.LogonSessionToken> LoginAsync(string email, string password, string mfaKey)
178+
{
179+
return this.client.LoginAsync(email, password, mfaKey);
180+
}
181+
167182
public Task<MegaApiClient.LogonSessionToken> LoginAsync(MegaApiClient.AuthInfos authInfos)
168183
{
169184
return this.client.LoginAsync(authInfos);
@@ -284,6 +299,11 @@ public Task<IEnumerable<INode>> GetNodesFromLinkAsync(Uri uri)
284299
return this.client.GenerateAuthInfosAsync(email, password);
285300
}
286301

302+
public Task<MegaApiClient.AuthInfos> GenerateAuthInfosAsync(string email, string password, string mfaKey)
303+
{
304+
return this.client.GenerateAuthInfosAsync(email, password, mfaKey);
305+
}
306+
287307
private T UnwrapException<T>(Func<T> action)
288308
{
289309
try

MegaApiClient/Interface/IMegaApiClient.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace CG.Web.MegaApiClient
1+
namespace CG.Web.MegaApiClient
22
{
33
using System;
44
using System.Collections.Generic;
@@ -13,6 +13,8 @@ public partial interface IMegaApiClient
1313

1414
MegaApiClient.LogonSessionToken Login(string email, string password);
1515

16+
MegaApiClient.LogonSessionToken Login(string email, string password, string mfaKey);
17+
1618
MegaApiClient.LogonSessionToken Login(MegaApiClient.AuthInfos authInfos);
1719

1820
void Login(MegaApiClient.LogonSessionToken logonSessionToken);
@@ -59,6 +61,6 @@ public partial interface IMegaApiClient
5961

6062
INode Rename(INode node, string newName);
6163

62-
MegaApiClient.AuthInfos GenerateAuthInfos(string email, string password);
64+
MegaApiClient.AuthInfos GenerateAuthInfos(string email, string password, string mfaKey = null);
6365
}
64-
}
66+
}

MegaApiClient/Interface/IMegaApiClientAsync.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace CG.Web.MegaApiClient
99

1010
public partial interface IMegaApiClient
1111
{
12-
Task<MegaApiClient.LogonSessionToken> LoginAsync(string email, string password);
12+
Task<MegaApiClient.LogonSessionToken> LoginAsync(string email, string password, string mfaKey = null);
1313

1414
Task<MegaApiClient.LogonSessionToken> LoginAsync(MegaApiClient.AuthInfos authInfos);
1515

@@ -58,6 +58,8 @@ public partial interface IMegaApiClient
5858
Task<IEnumerable<INode>> GetNodesFromLinkAsync(Uri uri);
5959

6060
Task<MegaApiClient.AuthInfos> GenerateAuthInfosAsync(string email, string password);
61+
62+
Task<MegaApiClient.AuthInfos> GenerateAuthInfosAsync(string email, string password, string mfaKey);
6163
}
6264
}
6365
#endif

MegaApiClient/MegaApiClient.cs

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,10 @@ public MegaApiClient(Options options, IWebClient webClient)
8888
/// </summary>
8989
/// <param name="email">email</param>
9090
/// <param name="password">password</param>
91+
/// <param name="mfaKey"></param>
9192
/// <returns><see cref="AuthInfos" /> object containing encrypted data</returns>
9293
/// <exception cref="ArgumentNullException">email or password is null</exception>
93-
public AuthInfos GenerateAuthInfos(string email, string password)
94+
public AuthInfos GenerateAuthInfos(string email, string password, string mfaKey = null)
9495
{
9596
if (string.IsNullOrEmpty(email))
9697
{
@@ -121,6 +122,14 @@ public AuthInfos GenerateAuthInfos(string email, string password)
121122
}
122123

123124
// Derived key contains master key (0-16) and password hash (16-32)
125+
if(!string.IsNullOrEmpty(mfaKey))
126+
{
127+
return new AuthInfos(
128+
email,
129+
derivedKeyBytes.Skip(16).ToArray().ToBase64(),
130+
derivedKeyBytes.Take(16).ToArray(),
131+
mfaKey);
132+
}
124133
return new AuthInfos(
125134
email,
126135
derivedKeyBytes.Skip(16).ToArray().ToBase64(),
@@ -136,7 +145,10 @@ public AuthInfos GenerateAuthInfos(string email, string password)
136145

137146
// Hash email and password to decrypt master key on Mega servers
138147
string hash = GenerateHash(email.ToLowerInvariant(), passwordAesKey);
139-
148+
if (!string.IsNullOrEmpty(mfaKey))
149+
{
150+
return new AuthInfos(email, hash, passwordAesKey, mfaKey);
151+
}
140152
return new AuthInfos(email, hash, passwordAesKey);
141153
}
142154
else
@@ -165,6 +177,20 @@ public LogonSessionToken Login(string email, string password)
165177
return this.Login(GenerateAuthInfos(email, password));
166178
}
167179

180+
/// <summary>
181+
/// Login to Mega.co.nz service using email/password credentials
182+
/// </summary>
183+
/// <param name="email">email</param>
184+
/// <param name="password">password</param>
185+
/// <param name="mfaKey"></param>
186+
/// <exception cref="ApiException">Service is not available or credentials are invalid</exception>
187+
/// <exception cref="ArgumentNullException">email or password is null</exception>
188+
/// <exception cref="NotSupportedException">Already logged in</exception>
189+
public LogonSessionToken Login(string email, string password, string mfaKey)
190+
{
191+
return this.Login(GenerateAuthInfos(email, password, mfaKey));
192+
}
193+
168194
/// <summary>
169195
/// Login to Mega.co.nz service using hashed credentials
170196
/// </summary>
@@ -183,7 +209,15 @@ public LogonSessionToken Login(AuthInfos authInfos)
183209
this.authenticatedLogin = true;
184210

185211
// Request Mega Api
186-
LoginRequest request = new LoginRequest(authInfos.Email, authInfos.Hash);
212+
LoginRequest request;
213+
if (!string.IsNullOrEmpty(authInfos.MFAKey))
214+
{
215+
request = new LoginRequest(authInfos.Email, authInfos.Hash, authInfos.MFAKey);
216+
}
217+
else
218+
{
219+
request = new LoginRequest(authInfos.Email, authInfos.Hash);
220+
}
187221
LoginResponse response = this.Request<LoginResponse>(request);
188222

189223
// Decrypt master key using our password key
@@ -1214,6 +1248,14 @@ public AuthInfos(string email, string hash, byte[] passwordAesKey)
12141248
this.PasswordAesKey = passwordAesKey;
12151249
}
12161250

1251+
public AuthInfos(string email, string hash, byte[] passwordAesKey, string mfaKey)
1252+
{
1253+
this.Email = email;
1254+
this.Hash = hash;
1255+
this.PasswordAesKey = passwordAesKey;
1256+
this.MFAKey = mfaKey;
1257+
}
1258+
12171259
[JsonProperty]
12181260
public string Email { get; private set; }
12191261

@@ -1222,6 +1264,9 @@ public AuthInfos(string email, string hash, byte[] passwordAesKey)
12221264

12231265
[JsonProperty]
12241266
public byte[] PasswordAesKey { get; private set; }
1267+
1268+
[JsonProperty]
1269+
public string MFAKey { get; private set; }
12251270
}
12261271

12271272
public class LogonSessionToken : IEquatable<LogonSessionToken>

MegaApiClient/MegaApiClientAsync.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ namespace CG.Web.MegaApiClient
99

1010
public partial class MegaApiClient : IMegaApiClient
1111
{
12-
#region Public async methods
12+
#region Public async methods
1313

14-
public Task<LogonSessionToken> LoginAsync(string email, string password)
14+
public Task<LogonSessionToken> LoginAsync(string email, string password, string mfaKey = null)
1515
{
16-
return Task.Run(() => this.Login(email, password));
16+
return Task.Run(() => this.Login(email, password, mfaKey));
1717
}
1818

1919
public Task<LogonSessionToken> LoginAsync(AuthInfos authInfos)
@@ -177,7 +177,12 @@ public Task<IEnumerable<INode>> GetNodesFromLinkAsync(Uri uri)
177177
return Task.Run(() => this.GenerateAuthInfos(email, password));
178178
}
179179

180-
#endregion
180+
public Task<MegaApiClient.AuthInfos> GenerateAuthInfosAsync(string email, string password, string mfaKey)
181+
{
182+
return Task.Run(() => this.GenerateAuthInfos(email, password, mfaKey));
183+
}
184+
185+
#endregion
181186
}
182187
}
183188
#endif

MegaApiClient/Serialization/Login.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,22 @@ public LoginRequest(string userHandle, string passwordHash)
1111
this.PasswordHash = passwordHash;
1212
}
1313

14+
public LoginRequest(string userHandle, string passwordHash, string mfaKey)
15+
: base("us")
16+
{
17+
this.UserHandle = userHandle;
18+
this.PasswordHash = passwordHash;
19+
this.MFAKey = mfaKey;
20+
}
21+
1422
[JsonProperty("user")]
1523
public string UserHandle { get; private set; }
1624

1725
[JsonProperty("uh")]
1826
public string PasswordHash { get; private set; }
27+
28+
[JsonProperty("mfa")]
29+
public string MFAKey { get; private set; }
1930
}
2031

2132
internal class LoginResponse

0 commit comments

Comments
 (0)