From a75710b5011dba2700779a2c95e28a77949b33bb Mon Sep 17 00:00:00 2001 From: ORelio Date: Wed, 23 May 2018 23:08:17 +0200 Subject: [PATCH] Fix online-mode server login Fix session checking on server login as described in issue #451 --- MinecraftClient/Program.cs | 1 + MinecraftClient/Protocol/ProtocolHandler.cs | 60 +++++++++++---------- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index 42b559a2..fb8d2549 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -262,6 +262,7 @@ namespace MinecraftClient case ProtocolHandler.LoginResult.AccountMigrated: failureMessage += "Account migrated, use e-mail as username."; break; case ProtocolHandler.LoginResult.ServiceUnavailable: failureMessage += "Login servers are unavailable. Please try again later."; break; case ProtocolHandler.LoginResult.WrongPassword: failureMessage += "Incorrect password, blacklisted IP or too many logins."; break; + case ProtocolHandler.LoginResult.InvalidResponse: failureMessage += "Invalid server response."; break; case ProtocolHandler.LoginResult.NotPremium: failureMessage += "User not premium."; break; case ProtocolHandler.LoginResult.OtherError: failureMessage += "Network error."; break; case ProtocolHandler.LoginResult.SSLError: failureMessage += "SSL Error."; break; diff --git a/MinecraftClient/Protocol/ProtocolHandler.cs b/MinecraftClient/Protocol/ProtocolHandler.cs index dae126e3..b0e0f047 100644 --- a/MinecraftClient/Protocol/ProtocolHandler.cs +++ b/MinecraftClient/Protocol/ProtocolHandler.cs @@ -205,16 +205,14 @@ namespace MinecraftClient.Protocol } } - public enum LoginResult { OtherError, ServiceUnavailable, SSLError, Success, WrongPassword, AccountMigrated, NotPremium, LoginRequired, InvalidToken, NullError }; + public enum LoginResult { OtherError, ServiceUnavailable, SSLError, Success, WrongPassword, AccountMigrated, NotPremium, LoginRequired, InvalidToken, InvalidResponse, NullError }; /// /// Allows to login to a premium Minecraft account using the Yggdrasil authentication scheme. /// /// Login /// Password - /// Will contain the access token returned by Minecraft.net, if the login is successful - /// Will contain the client token generated before sending to Minecraft.net - /// Will contain the player's PlayerID, needed for multiplayer + /// In case of successful login, will contain session information for multiplayer /// Returns the status of the login (Success, Failure, etc.) public static LoginResult GetLogin(string user, string pass, out SessionToken session) { @@ -233,13 +231,18 @@ namespace MinecraftClient.Protocol } else { - string[] temp = result.Split(new string[] { "accessToken\":\"" }, StringSplitOptions.RemoveEmptyEntries); - if (temp.Length >= 2) { session.ID = temp[1].Split('"')[0]; } - temp = result.Split(new string[] { "name\":\"" }, StringSplitOptions.RemoveEmptyEntries); - if (temp.Length >= 2) { session.PlayerName = temp[1].Split('"')[0]; } - temp = result.Split(new string[] { "availableProfiles\":[{\"id\":\"" }, StringSplitOptions.RemoveEmptyEntries); - if (temp.Length >= 2) { session.PlayerID = temp[1].Split('"')[0]; } - return LoginResult.Success; + Json.JSONData loginResponse = Json.ParseJson(result); + if (loginResponse.Properties.ContainsKey("accessToken") + && loginResponse.Properties.ContainsKey("selectedProfile") + && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("id") + && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("name")) + { + session.ID = loginResponse.Properties["accessToken"].StringValue; + session.PlayerID = loginResponse.Properties["selectedProfile"].Properties["id"].StringValue; + session.PlayerName = loginResponse.Properties["selectedProfile"].Properties["name"].StringValue; + return LoginResult.Success; + } + else return LoginResult.InvalidResponse; } } else if (code == 403) @@ -281,8 +284,7 @@ namespace MinecraftClient.Protocol /// /// Validates whether accessToken must be refreshed /// - /// Will contain the cached access token previously returned by Minecraft.net - /// Will contain the cached client token created on login + /// Session token to validate /// Returns the status of the token (Valid, Invalid, etc.) public static LoginResult GetTokenValidation(SessionToken session) { @@ -314,13 +316,11 @@ namespace MinecraftClient.Protocol /// Refreshes invalid token /// /// Login - /// Will contain the new access token returned by Minecraft.net, if the refresh is successful - /// Will contain the client token generated before sending to Minecraft.net - /// Will contain the player's PlayerID, needed for multiplayer + /// In case of successful token refresh, will contain session information for multiplayer /// Returns the status of the new token request (Success, Failure, etc.) - public static LoginResult GetNewToken(SessionToken currentsession, out SessionToken newsession) + public static LoginResult GetNewToken(SessionToken currentsession, out SessionToken session) { - newsession = new SessionToken(); + session = new SessionToken(); try { string result = ""; @@ -332,16 +332,20 @@ namespace MinecraftClient.Protocol { return LoginResult.NullError; } - else { - string[] temp = result.Split(new string[] { "accessToken\":\"" }, StringSplitOptions.RemoveEmptyEntries); - if (temp.Length >= 2) { newsession.ID = temp[1].Split('"')[0]; } - temp = result.Split(new string[] { "clientToken\":\"" }, StringSplitOptions.RemoveEmptyEntries); - if (temp.Length >= 2) { newsession.ClientID = temp[1].Split('"')[0]; } - temp = result.Split(new string[] { "name\":\"" }, StringSplitOptions.RemoveEmptyEntries); - if (temp.Length >= 2) { newsession.PlayerName = temp[1].Split('"')[0]; } - temp = result.Split(new string[] { "selectedProfile\":[{\"id\":\"" }, StringSplitOptions.RemoveEmptyEntries); - if (temp.Length >= 2) { newsession.PlayerID = temp[1].Split('"')[0]; } - return LoginResult.Success; + else + { + Json.JSONData loginResponse = Json.ParseJson(result); + if (loginResponse.Properties.ContainsKey("accessToken") + && loginResponse.Properties.ContainsKey("selectedProfile") + && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("id") + && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("name")) + { + session.ID = loginResponse.Properties["accessToken"].StringValue; + session.PlayerID = loginResponse.Properties["selectedProfile"].Properties["id"].StringValue; + session.PlayerName = loginResponse.Properties["selectedProfile"].Properties["name"].StringValue; + return LoginResult.Success; + } + else return LoginResult.InvalidResponse; } } else if (code == 403 && result.Contains("InvalidToken"))