/// <summary> /// Store a session and save it to disk if required. /// </summary> /// <param name="login">User login used with Minecraft.net</param> /// <param name="session">User session token used with Minecraft.net</param> public static void Store(string login, SessionToken session) { if (Contains(login)) { sessions[login] = session; } else { sessions.Add(login, session); } if (Settings.SessionCaching == CacheType.Disk && updatetimer.Enabled == true) { pendingadds.Add(new KeyValuePair<string, SessionToken>(login, session)); } else if (Settings.SessionCaching == CacheType.Disk) { SaveToDisk(); } }
/// <summary> /// Allows to login to a premium Minecraft account using the Yggdrasil authentication scheme. /// </summary> /// <param name="user">Login</param> /// <param name="pass">Password</param> /// <param name="accesstoken">Will contain the access token returned by Minecraft.net, if the login is successful</param> /// <param name="clienttoken">Will contain the client token generated before sending to Minecraft.net</param> /// <param name="uuid">Will contain the player's PlayerID, needed for multiplayer</param> /// <returns>Returns the status of the login (Success, Failure, etc.)</returns> public static LoginResult GetLogin(string user, string pass, out SessionToken session) { session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") }; try { string result = ""; string json_request = "{\"agent\": { \"name\": \"Minecraft\", \"version\": 1 }, \"username\": \"" + jsonEncode(user) + "\", \"password\": \"" + jsonEncode(pass) + "\", \"clientToken\": \"" + jsonEncode(session.ClientID) + "\" }"; int code = doHTTPSPost("authserver.mojang.com", "/authenticate", json_request, ref result); if (code == 200) { if (result.Contains("availableProfiles\":[]}")) { return LoginResult.NotPremium; } 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; } } else if (code == 403) { if (result.Contains("UserMigratedException")) { return LoginResult.AccountMigrated; } else return LoginResult.WrongPassword; } else if (code == 503) { return LoginResult.ServiceUnavailable; } else { ConsoleIO.WriteLineFormatted("§8Got error code from server: " + code); return LoginResult.OtherError; } } catch (System.Security.Authentication.AuthenticationException) { return LoginResult.SSLError; } catch (System.IO.IOException e) { if (e.Message.Contains("authentication")) { return LoginResult.SSLError; } else return LoginResult.OtherError; } catch { return LoginResult.OtherError; } }
/// <summary> /// Refreshes invalid token /// </summary> /// <param name="user">Login</param> /// <param name="accesstoken">Will contain the new access token returned by Minecraft.net, if the refresh is successful</param> /// <param name="clienttoken">Will contain the client token generated before sending to Minecraft.net</param> /// <param name="uuid">Will contain the player's PlayerID, needed for multiplayer</param> /// <returns>Returns the status of the new token request (Success, Failure, etc.)</returns> /// public static LoginResult GetNewToken(SessionToken currentsession, out SessionToken newsession) { newsession = new SessionToken(); try { string result = ""; string json_request = "{ \"accessToken\": \"" + jsonEncode(currentsession.ID) + "\", \"clientToken\": \"" + jsonEncode(currentsession.ClientID) + "\", \"selectedProfile\": { \"id\": \"" + jsonEncode(currentsession.PlayerID) + "\", \"name\": \"" + jsonEncode(currentsession.PlayerName) + "\" } }"; int code = doHTTPSPost("authserver.mojang.com", "/refresh", json_request, ref result); if (code == 200) { if (result == null) { 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 if (code == 403 && result.Contains("InvalidToken")) { return LoginResult.InvalidToken; } else { ConsoleIO.WriteLineFormatted("§8Got error code from server while refreshing authentication: " + code); return LoginResult.OtherError; } } catch { return LoginResult.OtherError; } }
/// <summary> /// Validates whether accessToken must be refreshed /// </summary> /// <param name="accesstoken">Will contain the cached access token previously returned by Minecraft.net</param> /// <param name="clienttoken">Will contain the cached client token created on login</param> /// <returns>Returns the status of the token (Valid, Invalid, etc.)</returns> /// public static LoginResult GetTokenValidation(SessionToken session) { try { string result = ""; string json_request = "{\"accessToken\": \"" + jsonEncode(session.ID) + "\", \"clientToken\": \"" + jsonEncode(session.ClientID) + "\" }"; int code = doHTTPSPost("authserver.mojang.com", "/validate", json_request, ref result); if (code == 204) { return LoginResult.Success; } else if (code == 403) { return LoginResult.LoginRequired; } else { return LoginResult.OtherError; } } catch { return LoginResult.OtherError; } }
/// <summary> /// Start a new Client /// </summary> private static void InitializeClient() { SessionToken session = new SessionToken(); ProtocolHandler.LoginResult result = ProtocolHandler.LoginResult.LoginRequired; if (Settings.Password == "-") { ConsoleIO.WriteLineFormatted("§8You chose to run in offline mode, how strange."); result = ProtocolHandler.LoginResult.Success; session.PlayerID = "0"; session.PlayerName = Settings.Login; } else { // Validate cached session or login new session. if (Settings.SessionCaching != CacheType.None && SessionCache.Contains(Settings.Login.ToLower())) { session = SessionCache.Get(Settings.Login.ToLower()); result = ProtocolHandler.GetTokenValidation(session); if (result != ProtocolHandler.LoginResult.Success) { ConsoleIO.WriteLineFormatted("§8Session expired. Maybe log in again?"); if (Settings.Password == "") RequestPassword(); } else ConsoleIO.WriteLineFormatted("§8This user is still logged into the app: " + session.PlayerName + '.'); } if (result != ProtocolHandler.LoginResult.Success) { Console.WriteLine("Automagically getting minecrafty magics... (connecting to Minecraft.net)"); result = ProtocolHandler.GetLogin(Settings.Login, Settings.Password, out session); if (result == ProtocolHandler.LoginResult.Success && Settings.SessionCaching != CacheType.None) { SessionCache.Store(Settings.Login.ToLower(), session); } } } if (result == ProtocolHandler.LoginResult.Success) { Settings.Username = session.PlayerName; if (Settings.ConsoleTitle != "") Console.Title = Settings.ExpandVars(Settings.ConsoleTitle); if (Settings.playerHeadAsIcon) ConsoleIcon.setPlayerIconAsync(Settings.Username); if (Settings.DebugMessages) Console.WriteLine("Success. Keep this session ID safe from the internet! No screencaps of it! (session ID: " + session.ID + ')'); //ProtocolHandler.RealmsListWorlds(Settings.Username, PlayerID, sessionID); //TODO REMOVE if (Settings.ServerIP == "") { Console.Write("Server IP : "); Settings.SetServerIP(Console.ReadLine()); } //Get server version int protocolversion = 0; ForgeInfo forgeInfo = null; if (Settings.ServerVersion != "" && Settings.ServerVersion.ToLower() != "auto") { protocolversion = Protocol.ProtocolHandler.MCVer2ProtocolVersion(Settings.ServerVersion); if (protocolversion != 0) { ConsoleIO.WriteLineFormatted("§8Using Minecraft version " + Settings.ServerVersion + " (protocol v" + protocolversion + ')'); } else ConsoleIO.WriteLineFormatted("§8Unknown or not supported MC version '" + Settings.ServerVersion + "'.\nSwitching to autodetection mode."); if (useMcVersionOnce) { useMcVersionOnce = false; Settings.ServerVersion = ""; } } if (protocolversion == 0) { Console.WriteLine("Fetchin' server profile..."); if (!ProtocolHandler.GetServerInfo(Settings.ServerIP, Settings.ServerPort, ref protocolversion, ref forgeInfo)) { HandleFailure("ZORK! Failed to plug this IP! Its derpy!", true, ChatBots.AutoRelog.DisconnectReason.ConnectionLost); return; } } if (protocolversion != 0) { try { //Start the main TCP client if (Settings.SingleCommand != "") { Client = new McTcpClient(session.PlayerName, session.PlayerID, session.ID, Settings.ServerIP, Settings.ServerPort, protocolversion, forgeInfo, Settings.SingleCommand); } else Client = new McTcpClient(session.PlayerName, session.PlayerID, session.ID, protocolversion, forgeInfo, Settings.ServerIP, Settings.ServerPort); //Update console title if (Settings.ConsoleTitle != "") Console.Title = Settings.ExpandVars(Settings.ConsoleTitle); } catch (NotSupportedException) { HandleFailure("ZORK! This minecraft server ve", true); } } else HandleFailure("ZORK! I coulden't tell what version this server was!", true); } else { Console.ForegroundColor = ConsoleColor.Gray; string failureMessage = "ZORK! Log in failed! : "; switch (result) { case ProtocolHandler.LoginResult.AccountMigrated: failureMessage += "You migrated to mojang! Use your e-mail instead of your username!"; break; case ProtocolHandler.LoginResult.ServiceUnavailable: failureMessage += "Apparently, Moajng's ninjas took their job too seriously. Login servers offline"; break; case ProtocolHandler.LoginResult.WrongPassword: failureMessage += "Incorrect password."; break; case ProtocolHandler.LoginResult.NotPremium: failureMessage += "User not premium. You had the trial! Maybe buy minecraft, is a totally kewl game!"; break; case ProtocolHandler.LoginResult.OtherError: failureMessage += "Your internet let me down. Networking error."; break; case ProtocolHandler.LoginResult.SSLError: failureMessage += "SSL Error."; break; default: failureMessage += "Well, gee bobby. I just can't figure out whats not working right."; break; } if (result == ProtocolHandler.LoginResult.SSLError && isUsingMono) { ConsoleIO.WriteLineFormatted("§8Oh, so I see your using Mono to run this app!." + '\n' + "The first time, you have to import HTTPS certificates using:" + '\n' + "mozroots --import --ask-remove"); return; } HandleFailure(failureMessage, false, ChatBot.DisconnectReason.LoginRejected); } }
private static LoginResult MicrosoftLogin(XboxLive.UserLoginResponse msaResponse, out SessionToken session) { session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") }; var ms = new XboxLive(); var mc = new MinecraftWithXbox(); try { var xblResponse = ms.XblAuthenticate(msaResponse); var xsts = ms.XSTSAuthenticate(xblResponse); // Might throw even password correct string accessToken = mc.LoginWithXbox(xsts.UserHash, xsts.Token); bool hasGame = mc.UserHasGame(accessToken); if (hasGame) { var profile = mc.GetUserProfile(accessToken); session.PlayerName = profile.UserName; session.PlayerID = profile.UUID; session.ID = accessToken; return(LoginResult.Success); } else { return(LoginResult.NotPremium); } } catch (Exception e) { ConsoleIO.WriteLineFormatted("§cMicrosoft authenticate failed: " + e.Message); if (Settings.DebugMessages) { ConsoleIO.WriteLineFormatted("§c" + e.StackTrace); } return(LoginResult.WrongPassword); // Might not always be wrong password } }
/// <summary> /// Sign-in to Microsoft Account by asking user to open sign-in page using browser. /// </summary> /// <remarks> /// The downside is this require user to copy and paste lengthy content from and to console. /// Sign-in page: 218 chars /// Response URL: around 1500 chars /// </remarks> /// <param name="session"></param> /// <returns></returns> public static LoginResult MicrosoftBrowserLogin(out SessionToken session) { var ms = new XboxLive(); string[] askOpenLink = { "Copy the following link to your browser and login to your Microsoft Account", ">>>>>>>>>>>>>>>>>>>>>>", "", ms.SignInUrl, "", "<<<<<<<<<<<<<<<<<<<<<<", "NOTICE: Once successfully logged in, you will see a blank page in your web browser.", "Copy the contents of your browser's address bar and paste it below to complete the login process.", }; ConsoleIO.WriteLine(string.Join("\n", askOpenLink)); string[] parts = { }; while (true) { string link = ConsoleIO.ReadLine(); if (string.IsNullOrEmpty(link)) { session = new SessionToken(); return(LoginResult.UserCancel); } parts = link.Split('#'); if (parts.Length < 2) { ConsoleIO.WriteLine("Invalid link. Please try again."); continue; } else { break; } } string hash = parts[1]; var dict = Request.ParseQueryString(hash); var msaResponse = new XboxLive.UserLoginResponse() { AccessToken = dict["access_token"], RefreshToken = dict["refresh_token"], ExpiresIn = int.Parse(dict["expires_in"]) }; try { return(MicrosoftLogin(msaResponse, out session)); } catch (Exception e) { session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") }; ConsoleIO.WriteLineFormatted("§cMicrosoft authenticate failed: " + e.Message); if (Settings.DebugMessages) { ConsoleIO.WriteLineFormatted("§c" + e.StackTrace); } return(LoginResult.WrongPassword); // Might not always be wrong password } }
/// <summary> /// Login using Mojang account. Will be outdated after account migration /// </summary> /// <param name="user"></param> /// <param name="pass"></param> /// <param name="session"></param> /// <returns></returns> private static LoginResult MojangLogin(string user, string pass, out SessionToken session) { session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") }; try { string result = ""; int code = 0; if (Settings.AccountType == AccountType.MCLeaks) { string json_request = "{\"token\": \"" + user + "\"}"; code = DoHTTPSPost("auth.mcleaks.net", "/v1/redeem", json_request, ref result); if (code == 200) { result = result.Split('\n')[1]; Json.JSONData loginResponse = Json.ParseJson(result); if (loginResponse.Properties["success"].StringValue == "true") { session.ID = loginResponse.Properties["result"].Properties["session"].StringValue; session.PlayerName = loginResponse.Properties["result"].Properties["mcname"].StringValue; string newResult = ""; DoHTTPSGet("api.mojang.com", "/users/profiles/minecraft/" + session.PlayerName, "", ref newResult); Json.JSONData UUIDJSON = Json.ParseJson(newResult); session.PlayerID = UUIDJSON.Properties["id"].StringValue; return(LoginResult.Success); } else { return(LoginResult.WrongPassword); } } else { return(LoginResult.OtherError); } } else { string json_request = "{\"agent\": { \"name\": \"Minecraft\", \"version\": 1 }, \"username\": \"" + JsonEncode(user) + "\", \"password\": \"" + JsonEncode(pass) + "\", \"clientToken\": \"" + JsonEncode(session.ClientID) + "\" }"; if (Settings.AccountType == AccountType.TheAltening) { code = DoHTTPPost("authserver.thealtening.com", "/authenticate", json_request, ref result); result = result.Split('\n')[1]; } else { code = DoHTTPSPost("authserver.mojang.com", "/authenticate", json_request, ref result); } if (code == 200) { if (result.Contains("availableProfiles\":[]}")) { return(LoginResult.NotPremium); } 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) { if (result.Contains("UserMigratedException")) { return(LoginResult.AccountMigrated); } else { return(LoginResult.WrongPassword); } } else if (code == 503) { return(LoginResult.ServiceUnavailable); } else { ConsoleIO.WriteLineFormatted(Translations.Get("error.http_code", code)); return(LoginResult.OtherError); } } } catch (System.Security.Authentication.AuthenticationException e) { if (Settings.DebugMessages) { ConsoleIO.WriteLineFormatted("§8" + e.ToString()); } return(LoginResult.SSLError); } catch (System.IO.IOException e) { if (Settings.DebugMessages) { ConsoleIO.WriteLineFormatted("§8" + e.ToString()); } if (e.Message.Contains("authentication")) { return(LoginResult.SSLError); } else { return(LoginResult.OtherError); } } catch (Exception e) { if (Settings.DebugMessages) { ConsoleIO.WriteLineFormatted("§8" + e.ToString()); } return(LoginResult.OtherError); } }
/// <summary> /// Allows to login to a premium Minecraft account using the Yggdrasil authentication scheme. /// </summary> /// <param name="user">Login</param> /// <param name="pass">Password</param> /// <param name="session">In case of successful login, will contain session information for multiplayer</param> /// <returns>Returns the status of the login (Success, Failure, etc.)</returns> public static LoginResult GetLogin(string user, string pass, AccountType type, out SessionToken session) { if (type == AccountType.Microsoft) { if (Settings.LoginMethod == "mcc") { return(MicrosoftMCCLogin(user, pass, out session)); } else { return(MicrosoftBrowserLogin(out session)); } } else if (type == AccountType.Mojang || type == AccountType.MCLeaks || type == AccountType.TheAltening) { return(MojangLogin(user, pass, out session)); } else { throw new InvalidOperationException("Account type must be Mojang, Microsoft, MCLeaks, or TheAltening."); } }