diff --git a/src/command_handler.h b/src/command_handler.h index a17999f..e4901f5 100644 --- a/src/command_handler.h +++ b/src/command_handler.h @@ -40,6 +40,7 @@ namespace globals extern bool g_FirstLogin; extern std::string g_TrustToken; extern bool g_TrustThisComputer; + extern std::string g_LoginToken; } // namespace globals #include "defines.h" @@ -161,6 +162,19 @@ bool handleLoginCommand(int8_t command, json& login_reply_json, uint32_t& accoun return false; } + // LOGIN_ERROR_LAUNCH_TOKEN_INVALID + case 0x0014: + { + // Single-use and short-lived; invalid means it was already used or expired. + globals::g_LoginToken.clear(); + xiloader::console::output(xiloader::color::error, "=========================================================="); + xiloader::console::output(xiloader::color::error, "Launch token invalid or expired (session expired)."); + xiloader::console::output(xiloader::color::error, "Please log in again from the launcher."); + xiloader::console::output(xiloader::color::error, "=========================================================="); + + return false; + } + // LOGIN_SUCCESS_CREATE_TOTP case 0x0010: { diff --git a/src/main.cpp b/src/main.cpp index 9e75c86..35d7422 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,6 +60,7 @@ namespace globals bool g_FirstLogin = false; // set to true when --user --pass are both set to allow for autologin std::string g_TrustToken = ""; // trust token loaded from disk or received from server bool g_TrustThisComputer = false; // user checkbox / CLI flag for "trust this computer" + std::string g_LoginToken = ""; // single-use launch token from the launcher; sent in place of password + OTP char* g_CharacterList = NULL; // Pointer to the character list data being sent from the server. bool g_IsRunning = false; // Flag to determine if the network threads should hault. @@ -569,8 +570,13 @@ int __cdecl main(int argc, char* argv[]) globals::g_Username = maybeUsername.value_or(globals::g_Username); globals::g_Password = maybePassword.value_or(globals::g_Password); - // Set autologin if it isn't set already - if (maybeUsername.has_value() && maybePassword.has_value()) + // Optional single-use launch token; sent in place of password + OTP. + globals::g_LoginToken = jsonGet(jsonData, "login_token").value_or(globals::g_LoginToken); + + // A launch-token boot has a username + token but no password, so + // it also enables autologin. + if (maybeUsername.has_value() && + (maybePassword.has_value() || !globals::g_LoginToken.empty())) { globals::g_FirstLogin = true; } diff --git a/src/network.cpp b/src/network.cpp index 7b6c3df..814713f 100644 --- a/src/network.cpp +++ b/src/network.cpp @@ -51,6 +51,7 @@ namespace globals extern bool g_FirstLogin; extern std::string g_TrustToken; extern bool g_TrustThisComputer; + extern std::string g_LoginToken; } // mbed tls state @@ -493,10 +494,17 @@ namespace xiloader login_json["version"] = globals::g_VersionNumber; login_json["command"] = command; - if (command == 0x10) // Only send trust fields for login + if (command == 0x10) // Only send trust/launch fields for login { login_json["trust_token"] = globals::g_TrustToken; login_json["trust_this_computer"] = globals::g_TrustThisComputer; + + // Optional single-use launch token; xi_connect consumes it in place + // of password + OTP (password may be empty). + if (!globals::g_LoginToken.empty()) + { + login_json["login_token"] = globals::g_LoginToken; + } } std::string str = login_json.dump();