diff --git a/Xenon/App.config b/Xenon/App.config
index 5c8886a..65c1d29 100644
--- a/Xenon/App.config
+++ b/Xenon/App.config
@@ -13,6 +13,9 @@
+
+
+
\ No newline at end of file
diff --git a/Xenon/MainWindow.xaml.cs b/Xenon/MainWindow.xaml.cs
index 34bae4a..d52f26c 100644
--- a/Xenon/MainWindow.xaml.cs
+++ b/Xenon/MainWindow.xaml.cs
@@ -129,23 +129,20 @@ private async void Button_Click(object sender, RoutedEventArgs e)
#endif
}
- private string encodeB64(string str) => Convert.ToBase64String(Encoding.UTF8.GetBytes(str));
-
private async Task performLogin(string username, string password)
{
var req = new HttpRequestMessage()
{
- RequestUri = new Uri("https://api.nexon.net/auth/login"),
+ RequestUri = new Uri("https://accounts.nexon.net/account/login/launcher"),
Method = HttpMethod.Post,
- Content = new StringContent($"{{\"allow_unverified\":true,\"user_id\":\"{username}\",\"user_pw\":\"{password}\"}}", Encoding.UTF8, "application/json")
+ Content = new StringContent($"{{\"id\":\"{username}\",\"password\":\"{Nexon.Auth.HashHexPassword(password)}\",\"auto_login\":false,\"client_id\":\"{Nexon.Auth.CLIENT_ID}\",\"scope\":\"{Nexon.Auth.SCOPE}\",\"device_id\":\"{Nexon.Auth.DeviceId}\"}}", Encoding.UTF8, "application/json")
};
req.Headers.Add("User-Agent", "NexonLauncher node-webkit/0.14.6 (Windows NT 10.0; WOW64) WebKit/537.36 (@c26c0312e940221c424c2730ef72be2c69ac1b67) nexon_client");
- req.Headers.Add("Authorization", "Basic " + encodeB64($"{username.Replace("@", "%40")}:{password}")); // nexon launcher does this, it uriencodes to %40 which is weird but meh
HttpResponseMessage res = await httpClient.SendAsync(req);
dynamic json = await parseResponseJson(res);
- nexonToken = json["token"];
+ nexonToken = json["access_token"];
cookieContainer.Add(new Cookie("nxtk", nexonToken, "/", ".nexon.net")); // this cookie is used in all future requests
}
@@ -162,7 +159,7 @@ private async Task isMapleUpToDate()
{
var req = new HttpRequestMessage()
{
- RequestUri = new Uri("https://api.nexon.net/products/10100"),
+ RequestUri = new Uri("https://api.nexon.io/products/10100"),
Method = HttpMethod.Get
};
addNexonLauncherData(req); // bearer auth + UA
@@ -199,7 +196,7 @@ private async Task getLaunchData()
// getting passport cookie to be able to get the launch token
var req = new HttpRequestMessage()
{
- RequestUri = new Uri("https://api.nexon.net/users/me/passport"),
+ RequestUri = new Uri("https://api.nexon.io/users/me/passport"),
Method = HttpMethod.Get
};
addNexonLauncherData(req);
@@ -261,6 +258,10 @@ private async Task parseResponseJson(HttpResponseMessage res)
{
// include err checking here
dynamic json = JObject.Parse(await res.Content.ReadAsStringAsync());
+
+ if (!res.IsSuccessStatusCode)
+ throw new Exception($"Nexon {res.StatusCode} error: \n\n{json["message"]} [{json["code"]}]");
+
if (json["error"] != null)
throw new Exception($"Nexon error: \n\n{json["error"]["message"]} [{json["error"]["code"]}]");
@@ -269,8 +270,8 @@ private async Task parseResponseJson(HttpResponseMessage res)
private void addNexonLauncherData(HttpRequestMessage req)
{
- req.Headers.Add("User-Agent", "NexonLauncher.nxl-17.02.03-219-5e3143f");
- req.Headers.Add("Authorization", "bearer " + encodeB64(nexonToken)); // this + cookie is used for auth here.
+ req.Headers.Add("User-Agent", "NexonLauncher.nxl-17.03.02-275-220ecfb");
+ req.Headers.Add("Authorization", "bearer " + Util.EncodeB64(nexonToken)); // this + cookie is used for auth here.
}
private void returnNoError()
diff --git a/Xenon/Nexon/Auth.cs b/Xenon/Nexon/Auth.cs
new file mode 100644
index 0000000..f3fa341
--- /dev/null
+++ b/Xenon/Nexon/Auth.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Security.Cryptography;
+
+namespace Xenon.Nexon
+{
+ public static class Auth
+ {
+ public const string CLIENT_ID = "7853644408";
+ public const string SCOPE = "us.launcher.all";
+
+ public static string DeviceId = ""; // lets just make a random 64 string that we use everytime...
+
+ static Auth()
+ {
+ DeviceId = Properties.Settings.Default.nexonDeviceId;
+
+ if (String.IsNullOrEmpty(DeviceId))
+ {
+ DeviceId = "";
+
+ var random = new Random();
+ for (int i = 0; i < 64; i++)
+ DeviceId += random.Next(16).ToString("X");
+
+ Properties.Settings.Default.nexonDeviceId = DeviceId;
+ Properties.Settings.Default.Save();
+ }
+ }
+
+ public static string HashHexPassword(string password)
+ {
+ // hashes the password as sha-512 and hex as required by nexon
+ // https://msdn.microsoft.com/en-us/library/s02tk69a(v=vs.110).aspx
+ SHA512 shaM = new SHA512Managed();
+ byte[] shaData = shaM.ComputeHash(Encoding.UTF8.GetBytes(password));
+
+ var sb = new StringBuilder();
+ for (int i = 0; i < shaData.Length; i++)
+ sb.Append(shaData[i].ToString("x2")); // hex formatted
+
+ return sb.ToString();
+ }
+ }
+}
diff --git a/Xenon/Properties/Settings.Designer.cs b/Xenon/Properties/Settings.Designer.cs
index 1ee556b..1e52bb6 100644
--- a/Xenon/Properties/Settings.Designer.cs
+++ b/Xenon/Properties/Settings.Designer.cs
@@ -12,7 +12,7 @@ namespace Xenon.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.1.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -34,5 +34,17 @@ public string username {
this["username"] = value;
}
}
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("")]
+ public string nexonDeviceId {
+ get {
+ return ((string)(this["nexonDeviceId"]));
+ }
+ set {
+ this["nexonDeviceId"] = value;
+ }
+ }
}
}
diff --git a/Xenon/Properties/Settings.settings b/Xenon/Properties/Settings.settings
index 9c59d40..1bbfd96 100644
--- a/Xenon/Properties/Settings.settings
+++ b/Xenon/Properties/Settings.settings
@@ -5,5 +5,8 @@
+
+
+
\ No newline at end of file
diff --git a/Xenon/Util.cs b/Xenon/Util.cs
new file mode 100644
index 0000000..e186701
--- /dev/null
+++ b/Xenon/Util.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Xenon
+{
+ static class Util
+ {
+ public static string EncodeB64(string plainText)
+ => System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(plainText));
+
+ public static string DecodeB64(string base64EncodedData)
+ => System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(base64EncodedData));
+ }
+}
diff --git a/Xenon/Xenon.csproj b/Xenon/Xenon.csproj
index 8fe93c4..5e7a503 100644
--- a/Xenon/Xenon.csproj
+++ b/Xenon/Xenon.csproj
@@ -61,6 +61,8 @@
+
+
MSBuild:Compile
Designer
@@ -114,6 +116,7 @@
+