From a7b081075237e30f993f0a3a15d25842840701e9 Mon Sep 17 00:00:00 2001 From: MKuijpers Date: Sat, 15 Apr 2023 19:57:14 +0200 Subject: [PATCH 1/2] Add fetchToken method to fetch access token using the new OP Auth flow --- Api/Api.as | 138 +++++++++++++++++++++++++++++++++++----------- UI/AuthWindow.as | 2 +- Utils/Settings.as | 3 + 3 files changed, 111 insertions(+), 32 deletions(-) diff --git a/Api/Api.as b/Api/Api.as index e1cd0d3..75a8173 100644 --- a/Api/Api.as +++ b/Api/Api.as @@ -1,4 +1,46 @@ namespace Api { + + void fetchAccessTokenAsync() { + if (AccessToken != "") { + print("Access token already set, skipping authentication."); + return; + } + + // Start the task to get the token from Openplanet + print("Getting token from Openplanet"); + Auth::PluginAuthTask@ tokenTask = Auth::GetToken(); + + // Wait until the task has finished + while (!tokenTask.Finished()) { + yield(); + } + + // Take the token + string token = tokenTask.Token(); + trace("Token: \"" + token + "\""); + + // Send it to the shoutbox server + Net::HttpRequest@ req = Net::HttpPost( + ApiUrl + "/auth/login/plugin", + "token=" + Net::UrlEncode(token) + ); + while (!req.Finished()) { + yield(); + } + + if (req.ResponseCode() != 200 && req.ResponseCode() != 201) { + error("Unable to authenticate, http error " + req.ResponseCode()); + return; + } + + // Parse the server response + Json::Value json = Json::Parse(req.String()); + + // Keep track of our information, including a secret that we can use to authenticate ourselves with the shoutbox server + // g_accountID = js["account_id"]; + // g_displayName = js["display_name"]; + AccessToken = json["access_token"]; + } // Workaround method for checkServer to ensure checkServer is only called when webId and playerLogin are not the equal void checkServerWaitForValidWebId() { @@ -12,47 +54,81 @@ namespace Api { void checkServer() { g_dojo.checkingServer = true; + g_dojo.playerName = g_dojo.network.PlayerInfo.Name; g_dojo.playerLogin = g_dojo.network.PlayerInfo.Login; g_dojo.webId = g_dojo.network.PlayerInfo.WebServicesUserId; - Net::HttpRequest@ auth = Net::HttpGet(ApiUrl + "/auth?name=" + g_dojo.playerName + "&login=" + g_dojo.playerLogin + "&webid=" + g_dojo.webId + "&sessionId=" + SessionId + "&pluginVersion=" + g_dojo.version); - while (!auth.Finished()) { + fetchAccessTokenAsync(); + + Net::HttpRequest@ req = Net::HttpRequest(); + req.Method = Net::HttpMethod::Get; + req.Url = ApiUrl + "/auth/me"; + req.Headers["Authorization"] = "Bearer " + AccessToken; + req.Start(); + + print("Starting /auth/me request..."); + while (!req.Finished()) { yield(); - sleep(50); } - if (auth.String().get_Length() > 0) { - Json::Value json = Json::Parse(auth.String()); - - if (json.GetType() != Json::Type::Null) { - print("HasKey authUrl: " + json.HasKey("authURL")); - print("HasKey authSuccess: " + json.HasKey("authSuccess")); - - if (json.HasKey("authURL")) { - try { - g_dojo.pluginAuthUrl = json["authURL"]; - ClientCode = json["clientCode"]; - SessionId = ""; - UI::ShowNotification("TMDojo", "Plugin needs authentication!\n\nF3 → Scripts → TMDojo → Authenticate Plugin", 10000); - } catch { - error("checkServer json error"); - } - } - if (json.HasKey("authSuccess")) { - g_dojo.pluginAuthed = true; - UI::ShowNotification("TMDojo", "Plugin is authenticated!", SUCCESS_COLOR); - } - } else { - UI::ShowNotification("TMDojo", "checkServer() Error: Json response is null", ERROR_COLOR); - } - - g_dojo.serverAvailable = true; + + if (req.ResponseCode() == 200) { + g_dojo.pluginAuthed = true; + print("Plugin authenticated!"); + UI::ShowNotification("TMDojo", "Plugin authenticated!", SUCCESS_COLOR); } else { - g_dojo.serverAvailable = false; + g_dojo.pluginAuthed = false; + print("Plugin not authenticated!"); } + + g_dojo.serverAvailable = req.String().Length > 0; + g_dojo.checkingServer = false; } + // void checkServer() { + // g_dojo.checkingServer = true; + // g_dojo.playerName = g_dojo.network.PlayerInfo.Name; + // g_dojo.playerLogin = g_dojo.network.PlayerInfo.Login; + // g_dojo.webId = g_dojo.network.PlayerInfo.WebServicesUserId; + + // Net::HttpRequest@ auth = Net::HttpGet(ApiUrl + "/auth?name=" + g_dojo.playerName + "&login=" + g_dojo.playerLogin + "&webid=" + g_dojo.webId + "&sessionId=" + SessionId + "&pluginVersion=" + g_dojo.version); + // while (!auth.Finished()) { + // yield(); + // sleep(50); + // } + // if (auth.String().get_Length() > 0) { + // Json::Value json = Json::Parse(auth.String()); + + // if (json.GetType() != Json::Type::Null) { + // print("HasKey authUrl: " + json.HasKey("authURL")); + // print("HasKey authSuccess: " + json.HasKey("authSuccess")); + + // if (json.HasKey("authURL")) { + // try { + // g_dojo.pluginAuthUrl = json["authURL"]; + // ClientCode = json["clientCode"]; + // SessionId = ""; + // UI::ShowNotification("TMDojo", "Plugin needs authentication!\n\nF3 → Scripts → TMDojo → Authenticate Plugin", 10000); + // } catch { + // error("checkServer json error"); + // } + // } + // if (json.HasKey("authSuccess")) { + // g_dojo.pluginAuthed = true; + // UI::ShowNotification("TMDojo", "Plugin is authenticated!", SUCCESS_COLOR); + // } + // } else { + // UI::ShowNotification("TMDojo", "checkServer() Error: Json response is null", ERROR_COLOR); + // } + + // g_dojo.serverAvailable = true; + // } else { + // g_dojo.serverAvailable = false; + // } + // g_dojo.checkingServer = false; + // } + void logout() { string logoutBody = "{\"sessionId\":\"" + SessionId + "\"}"; Net::HttpRequest@ req = Net::HttpPost(ApiUrl + "/logout?pluginVersion=" + g_dojo.version, logoutBody, "application/json"); @@ -72,7 +148,7 @@ namespace Api { } } - void authenticatePlugin() { + void authenticatePluginWithBrowser() { OpenBrowserURL(g_dojo.pluginAuthUrl); startnew(getPluginAuth); } diff --git a/UI/AuthWindow.as b/UI/AuthWindow.as index 1837b02..9fdcaf6 100644 --- a/UI/AuthWindow.as +++ b/UI/AuthWindow.as @@ -12,7 +12,7 @@ void renderAuthWindow() { UI::Text("If it takes a bit longer, you can just press the button again (if you're already logged in, it's just gonna take a second)."); UI::Text(""); if (!g_dojo.isAuthenticating && UI::Button("Authenticate Plugin")) { - Api::authenticatePlugin(); + Api::authenticatePluginWithBrowser(); } if (g_dojo.isAuthenticating) { UI::Text("Awaiting authentication, " + (MAX_CHECK_SESSION_ID - g_dojo.checkSessionIdCount) + " seconds remaining"); diff --git a/Utils/Settings.as b/Utils/Settings.as index 3749c05..d159c71 100644 --- a/Utils/Settings.as +++ b/Utils/Settings.as @@ -16,6 +16,9 @@ string ClientCode = ""; [Setting password name="TMDojoSessionId" description="TMDojo plugin SessionId"] string SessionId = ""; +[Setting password name="TMDojoAccessToken" description="TMDojo plugin AccessToken"] +string AccessToken = ""; + [Setting name="TMDojoDebugOverlayEnabled" description="Enable / Disable debug overlay"] bool DebugOverlayEnabled = false; From 06bf039cc1b3b881790e3cd666cf21f8a0b3e3f0 Mon Sep 17 00:00:00 2001 From: MKuijpers Date: Sat, 15 Apr 2023 20:21:31 +0200 Subject: [PATCH 2/2] Cleanup API requests and remove old auth code --- Api/Api.as | 121 +++++++++++----------------------------------- Lib/Dojo.as | 10 ++-- UI/AuthWindow.as | 5 +- UI/Menu.as | 6 +-- Utils/Settings.as | 6 --- 5 files changed, 35 insertions(+), 113 deletions(-) diff --git a/Api/Api.as b/Api/Api.as index 75a8173..40ab012 100644 --- a/Api/Api.as +++ b/Api/Api.as @@ -21,7 +21,7 @@ namespace Api { // Send it to the shoutbox server Net::HttpRequest@ req = Net::HttpPost( - ApiUrl + "/auth/login/plugin", + ApiUrl + "/auth/login/plugin" + "?pluginVersion=" + g_dojo.version, "token=" + Net::UrlEncode(token) ); while (!req.Finished()) { @@ -43,16 +43,16 @@ namespace Api { } // Workaround method for checkServer to ensure checkServer is only called when webId and playerLogin are not the equal - void checkServerWaitForValidWebId() { + void authenticatePluginWaitForValidWebId() { while (g_dojo.network.PlayerInfo.Login == g_dojo.network.PlayerInfo.WebServicesUserId) { sleep(50); yield(); } - startnew(Api::checkServer); + startnew(Api::authenticatePlugin); } - void checkServer() { + void authenticatePlugin() { g_dojo.checkingServer = true; g_dojo.playerName = g_dojo.network.PlayerInfo.Name; @@ -63,7 +63,7 @@ namespace Api { Net::HttpRequest@ req = Net::HttpRequest(); req.Method = Net::HttpMethod::Get; - req.Url = ApiUrl + "/auth/me"; + req.Url = ApiUrl + "/auth/me" + "?pluginVersion=" + g_dojo.version; req.Headers["Authorization"] = "Bearer " + AccessToken; req.Start(); @@ -75,10 +75,15 @@ namespace Api { if (req.ResponseCode() == 200) { g_dojo.pluginAuthed = true; print("Plugin authenticated!"); - UI::ShowNotification("TMDojo", "Plugin authenticated!", SUCCESS_COLOR); + + // Parse server response and display welcome message + Json::Value json = Json::Parse(req.String()); + string playerName = json["playerName"]; + UI::ShowNotification("TMDojo", "Plugin authenticated!\n\nWelcome, " + playerName + "!", SUCCESS_COLOR); } else { g_dojo.pluginAuthed = false; - print("Plugin not authenticated!"); + UI::ShowNotification("TMDojo", "Plugin authentication failed. Error code: " + req.ResponseCode(), ERROR_COLOR); + print("Plugin authentication failed, status code: " + req.ResponseCode()); } g_dojo.serverAvailable = req.String().Length > 0; @@ -86,100 +91,30 @@ namespace Api { g_dojo.checkingServer = false; } - // void checkServer() { - // g_dojo.checkingServer = true; - // g_dojo.playerName = g_dojo.network.PlayerInfo.Name; - // g_dojo.playerLogin = g_dojo.network.PlayerInfo.Login; - // g_dojo.webId = g_dojo.network.PlayerInfo.WebServicesUserId; - - // Net::HttpRequest@ auth = Net::HttpGet(ApiUrl + "/auth?name=" + g_dojo.playerName + "&login=" + g_dojo.playerLogin + "&webid=" + g_dojo.webId + "&sessionId=" + SessionId + "&pluginVersion=" + g_dojo.version); - // while (!auth.Finished()) { - // yield(); - // sleep(50); - // } - // if (auth.String().get_Length() > 0) { - // Json::Value json = Json::Parse(auth.String()); - - // if (json.GetType() != Json::Type::Null) { - // print("HasKey authUrl: " + json.HasKey("authURL")); - // print("HasKey authSuccess: " + json.HasKey("authSuccess")); - - // if (json.HasKey("authURL")) { - // try { - // g_dojo.pluginAuthUrl = json["authURL"]; - // ClientCode = json["clientCode"]; - // SessionId = ""; - // UI::ShowNotification("TMDojo", "Plugin needs authentication!\n\nF3 → Scripts → TMDojo → Authenticate Plugin", 10000); - // } catch { - // error("checkServer json error"); - // } - // } - // if (json.HasKey("authSuccess")) { - // g_dojo.pluginAuthed = true; - // UI::ShowNotification("TMDojo", "Plugin is authenticated!", SUCCESS_COLOR); - // } - // } else { - // UI::ShowNotification("TMDojo", "checkServer() Error: Json response is null", ERROR_COLOR); - // } - - // g_dojo.serverAvailable = true; - // } else { - // g_dojo.serverAvailable = false; - // } - // g_dojo.checkingServer = false; - // } - void logout() { - string logoutBody = "{\"sessionId\":\"" + SessionId + "\"}"; - Net::HttpRequest@ req = Net::HttpPost(ApiUrl + "/logout?pluginVersion=" + g_dojo.version, logoutBody, "application/json"); + // Setup logout request + Net::HttpRequest@ req = Net::HttpRequest(); + req.Method = Net::HttpMethod::Post; + req.Url = ApiUrl + "/auth/logout" + "?pluginVersion=" + g_dojo.version; + req.Headers["Authorization"] = "Bearer " + AccessToken; + req.Start(); + + // Wait until the request has finished while (!req.Finished()) { yield(); - sleep(50); } + // Notify user if logout failed int status = req.ResponseCode(); - if (status == 200) { - UI::ShowNotification("TMDojo", "Plugin logged out!", SUCCESS_COLOR); - SessionId = ""; - g_dojo.pluginAuthed = false; - startnew(Api::checkServerWaitForValidWebId); - } else { + if (status != 200 && status != 201) { UI::ShowNotification("TMDojo", "Failed to logout, please try again!", ERROR_COLOR); + return; } - } - - void authenticatePluginWithBrowser() { - OpenBrowserURL(g_dojo.pluginAuthUrl); - startnew(getPluginAuth); - } - void getPluginAuth() { - g_dojo.isAuthenticating = true; - while (g_dojo.checkSessionIdCount < MAX_CHECK_SESSION_ID) { - sleep(1000); - g_dojo.checkSessionIdCount++; - Net::HttpRequest@ auth = Net::HttpGet(ApiUrl + "/auth/pluginSecret?clientCode=" + ClientCode+ "&pluginVersion=" + g_dojo.version); - while (!auth.Finished()) { - yield(); - sleep(50); - } - try { - Json::Value json = Json::Parse(auth.String()); - SessionId = json["sessionId"]; - UI::ShowNotification("TMDojo", "Plugin is authenticated!", SUCCESS_COLOR, 10000); - g_dojo.pluginAuthed = true; - g_dojo.checkSessionIdCount = 0; - ClientCode = ""; - break; - } catch { - - } - } - g_dojo.isAuthenticating = false; - if (g_dojo.checkSessionIdCount >= MAX_CHECK_SESSION_ID) { - UI::ShowNotification("TMDojo", "Plugin authentication took too long, please try again", ERROR_COLOR, 10000); - g_dojo.checkSessionIdCount = 0; - } + // Notify user of logout and update fields + UI::ShowNotification("TMDojo", "Plugin logged out!", SUCCESS_COLOR); + AccessToken = ""; + g_dojo.pluginAuthed = false; } // Parse list of uint values as a string joined by a comma delimiter @@ -269,7 +204,7 @@ namespace Api { // Build headers dictionary@ Headers = dictionary(); - Headers["Authorization"] = "dojo " + SessionId; + Headers["Authorization"] = "Bearer " + AccessToken; Headers["Content-Type"] = "application/octet-stream"; @req.Headers = Headers; diff --git a/Lib/Dojo.as b/Lib/Dojo.as index a71c12c..aaa5fe9 100644 --- a/Lib/Dojo.as +++ b/Lib/Dojo.as @@ -24,10 +24,6 @@ class TMDojo bool serverAvailable = false; bool checkingServer = false; - // Session - int checkSessionIdCount = 0; - int maxCheckSessionId = 60; - // Plugin info Meta::Plugin@ plugin = Meta::ExecutingPlugin(); string version = plugin.Version; @@ -41,7 +37,7 @@ class TMDojo TMDojo() { auto app = GetApp(); @network = cast(app.Network); - startnew(Api::checkServerWaitForValidWebId); + startnew(Api::authenticatePluginWaitForValidWebId); } void Reset() { @@ -112,8 +108,8 @@ class TMDojo void Update(float dt) { - // Do not start recording if the user is not authenticated or doesn't even have a SessionId - if (!pluginAuthed || SessionId == "") { + // Do not start recording if the user is not authenticated or doesn't have a AccessToken + if (!pluginAuthed || AccessToken == "") { return; } diff --git a/UI/AuthWindow.as b/UI/AuthWindow.as index 9fdcaf6..ca825d0 100644 --- a/UI/AuthWindow.as +++ b/UI/AuthWindow.as @@ -12,10 +12,7 @@ void renderAuthWindow() { UI::Text("If it takes a bit longer, you can just press the button again (if you're already logged in, it's just gonna take a second)."); UI::Text(""); if (!g_dojo.isAuthenticating && UI::Button("Authenticate Plugin")) { - Api::authenticatePluginWithBrowser(); - } - if (g_dojo.isAuthenticating) { - UI::Text("Awaiting authentication, " + (MAX_CHECK_SESSION_ID - g_dojo.checkSessionIdCount) + " seconds remaining"); + startnew(Api::authenticatePlugin); } } else { UI::Text(GREEN + "Plugin authed!"); diff --git a/UI/Menu.as b/UI/Menu.as index 576e3e7..a766e3c 100644 --- a/UI/Menu.as +++ b/UI/Menu.as @@ -23,7 +23,7 @@ void RenderMenu() if (UI::MenuItem(Enabled ? "Turn OFF" : "Turn ON", "", false, true)) { Enabled = !Enabled; if (Enabled) { - startnew(Api::checkServerWaitForValidWebId); + startnew(Api::authenticatePluginWaitForValidWebId); } } @@ -32,7 +32,7 @@ void RenderMenu() if (DevMode && UI::MenuItem("Switch to " + otherApi + " " + otherUi , "", false, true)) { ApiUrl = otherApi; UiUrl = otherUi; - startnew(Api::checkServerWaitForValidWebId); + startnew(Api::authenticatePluginWaitForValidWebId); } if (UI::MenuItem(CheckMarkWithLabel(OverlayEnabled, "Overlay"), "", false, true)) { @@ -49,7 +49,7 @@ void RenderMenu() if (!g_dojo.serverAvailable && !g_dojo.checkingServer) { if (UI::MenuItem("Check server", "", false, true)) { - startnew(Api::checkServerWaitForValidWebId); + startnew(Api::authenticatePluginWaitForValidWebId); } } diff --git a/Utils/Settings.as b/Utils/Settings.as index d159c71..028b664 100644 --- a/Utils/Settings.as +++ b/Utils/Settings.as @@ -10,12 +10,6 @@ string ApiUrl = REMOTE_API; [Setting name="TMDojoUiUrl" description="TMDojo Ui Url"] string UiUrl = REMOTE_UI; -[Setting password name="TMDojoClientCode" description="TMDojo plugin Client Code"] -string ClientCode = ""; - -[Setting password name="TMDojoSessionId" description="TMDojo plugin SessionId"] -string SessionId = ""; - [Setting password name="TMDojoAccessToken" description="TMDojo plugin AccessToken"] string AccessToken = "";