mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-27 04:42:33 +00:00
Fix a regression issue orca couldn't login into BBL cloud (#13407)
* Add ticket-based OAuth flow for third-party login in HttpServer * Fix legacy plugin login
This commit is contained in:
@@ -2502,7 +2502,7 @@ std::map<std::string, std::string> GUI_App::get_extra_header()
|
||||
std::map<std::string, std::string> extra_headers;
|
||||
extra_headers.insert(std::make_pair("X-BBL-Client-Type", "slicer"));
|
||||
extra_headers.insert(std::make_pair("X-BBL-Client-Name", SLIC3R_APP_NAME));
|
||||
extra_headers.insert(std::make_pair("X-BBL-Client-Version", VersionInfo::convert_full_version(SLIC3R_VERSION)));
|
||||
extra_headers.insert(std::make_pair("X-BBL-Client-Version", get_bbl_client_version()));
|
||||
#if defined(__WINDOWS__)
|
||||
#ifdef _M_X64
|
||||
extra_headers.insert(std::make_pair("X-BBL-OS-Type", "windows"));
|
||||
@@ -2524,6 +2524,16 @@ std::map<std::string, std::string> GUI_App::get_extra_header()
|
||||
return extra_headers;
|
||||
}
|
||||
|
||||
std::string GUI_App::get_bbl_client_version()
|
||||
{
|
||||
if (BBLNetworkPlugin::instance().get_get_my_token() == nullptr) {
|
||||
// Legacy Bambu plugin lacks bambu_network_get_my_token. Pin client
|
||||
// version so the auth server keeps using the ?access_token= redirect.
|
||||
return "01.10.01.50";
|
||||
}
|
||||
return VersionInfo::convert_full_version(SLIC3R_VERSION);
|
||||
}
|
||||
|
||||
//BBS
|
||||
void GUI_App::init_http_extra_header()
|
||||
{
|
||||
|
||||
@@ -707,6 +707,10 @@ public:
|
||||
bool hot_reload_network_plugin();
|
||||
std::string get_latest_network_version() const;
|
||||
bool has_network_update_available() const;
|
||||
// Return the client version to report to Bambu servers. Pinned to
|
||||
// 01.10.01.50 when the legacy network plugin lacks get_my_token support
|
||||
// so the auth server stays on the ?access_token= redirect path.
|
||||
std::string get_bbl_client_version();
|
||||
|
||||
private:
|
||||
int updating_bambu_networking();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "GUI_App.hpp"
|
||||
#include "slic3r/Utils/Http.hpp"
|
||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||
#include "slic3r/Utils/BBLNetworkPlugin.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
@@ -268,9 +269,106 @@ std::shared_ptr<HttpServer::Response> HttpServer::bbl_auth_handle_request(const
|
||||
std::string location_str = (boost::format("%1%?result=fail&error=%2%") % redirect_url % error_str).str();
|
||||
return std::make_shared<ResponseRedirect>(location_str);
|
||||
}
|
||||
} else {
|
||||
return std::make_shared<ResponseNotFound>();
|
||||
}
|
||||
|
||||
// Ticket-based redirect: Bambu Lab's auth server redirects here after a
|
||||
// third-party (Google) OAuth so that the access token never travels through
|
||||
// the URL. We exchange the ticket via the network plugin's get_my_token,
|
||||
// then run the same get_my_profile + change_user flow as access_token.
|
||||
// Skip entirely on legacy plugins missing bambu_network_get_my_token —
|
||||
// those clients pin X-BBL-Client-Version so the server stays on the legacy
|
||||
// ?access_token= redirect path and never sends ?ticket= here.
|
||||
const std::string ticket = url_get_param(url, "ticket");
|
||||
const std::string ticket_redirect_url = url_get_param(url, "redirect_url");
|
||||
if (!ticket.empty() && !ticket_redirect_url.empty() &&
|
||||
BBLNetworkPlugin::instance().get_get_my_token() != nullptr) {
|
||||
BOOST_LOG_TRIVIAL(info) << "thirdparty_login: ticket flow";
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
if (!agent) {
|
||||
std::string location_str = (boost::format("%1%?result=fail&error=no_agent") % ticket_redirect_url).str();
|
||||
return std::make_shared<ResponseRedirect>(location_str);
|
||||
}
|
||||
|
||||
auto fail_redirect = [&ticket_redirect_url](const std::string& reason) {
|
||||
std::string location_str = (boost::format("%1%?result=fail&error=%2%") % ticket_redirect_url % reason).str();
|
||||
return std::make_shared<ResponseRedirect>(location_str);
|
||||
};
|
||||
|
||||
unsigned int token_http_code = 0;
|
||||
std::string token_body;
|
||||
int token_result = agent->get_my_token(ticket, &token_http_code, &token_body);
|
||||
if (token_result != 0) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "thirdparty_login: get_my_token failed, http_code=" << token_http_code;
|
||||
return fail_redirect("get_my_token_error_" + std::to_string(token_result));
|
||||
}
|
||||
|
||||
std::string access_token;
|
||||
std::string refresh_token;
|
||||
std::string expires_in_str;
|
||||
std::string refresh_expires_in_str;
|
||||
try {
|
||||
json token_j = json::parse(token_body);
|
||||
if (token_j.contains("accessToken"))
|
||||
access_token = token_j["accessToken"].get<std::string>();
|
||||
if (token_j.contains("refreshToken"))
|
||||
refresh_token = token_j["refreshToken"].get<std::string>();
|
||||
if (token_j.contains("expiresIn"))
|
||||
expires_in_str = std::to_string(token_j["expiresIn"].get<double>());
|
||||
if (token_j.contains("refreshExpiresIn"))
|
||||
refresh_expires_in_str = std::to_string(token_j["refreshExpiresIn"].get<double>());
|
||||
} catch (...) {
|
||||
return fail_redirect("token_parse_error");
|
||||
}
|
||||
|
||||
if (access_token.empty()) {
|
||||
return fail_redirect("token_missing");
|
||||
}
|
||||
|
||||
unsigned int profile_http_code = 0;
|
||||
std::string profile_body;
|
||||
int profile_result = agent->get_my_profile(access_token, &profile_http_code, &profile_body);
|
||||
if (profile_result != 0) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "thirdparty_login: get_my_profile failed, http_code=" << profile_http_code;
|
||||
return fail_redirect("get_user_profile_error_" + std::to_string(profile_result));
|
||||
}
|
||||
|
||||
std::string user_id;
|
||||
std::string user_name;
|
||||
std::string user_account;
|
||||
std::string user_avatar;
|
||||
try {
|
||||
json user_j = json::parse(profile_body);
|
||||
if (user_j.contains("uidStr"))
|
||||
user_id = user_j["uidStr"].get<std::string>();
|
||||
if (user_j.contains("name"))
|
||||
user_name = user_j["name"].get<std::string>();
|
||||
if (user_j.contains("avatar"))
|
||||
user_avatar = user_j["avatar"].get<std::string>();
|
||||
if (user_j.contains("account"))
|
||||
user_account = user_j["account"].get<std::string>();
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "thirdparty_login: profile JSON parse failed";
|
||||
}
|
||||
|
||||
json j;
|
||||
j["data"]["refresh_token"] = refresh_token;
|
||||
j["data"]["token"] = access_token;
|
||||
j["data"]["expires_in"] = expires_in_str;
|
||||
j["data"]["refresh_expires_in"] = refresh_expires_in_str;
|
||||
j["data"]["user"]["uid"] = user_id;
|
||||
j["data"]["user"]["name"] = user_name;
|
||||
j["data"]["user"]["account"] = user_account;
|
||||
j["data"]["user"]["avatar"] = user_avatar;
|
||||
agent->change_user(j.dump());
|
||||
if (agent->is_user_login()) {
|
||||
wxGetApp().request_user_login(1);
|
||||
}
|
||||
GUI::wxGetApp().CallAfter([] { wxGetApp().ShowUserLogin(false); });
|
||||
std::string location_str = (boost::format("%1%?result=success") % ticket_redirect_url).str();
|
||||
return std::make_shared<ResponseRedirect>(location_str);
|
||||
}
|
||||
|
||||
return std::make_shared<ResponseNotFound>();
|
||||
}
|
||||
|
||||
void HttpServer::ResponseNotFound::write_response(std::stringstream& ssOut)
|
||||
|
||||
@@ -84,7 +84,7 @@ ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_AN
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "login url = " << TargetUrl.ToStdString();
|
||||
|
||||
m_bbl_user_agent = wxString::Format("BBL-Slicer/v%s", SLIC3R_VERSION);
|
||||
m_bbl_user_agent = wxString::Format("BBL-Slicer/v%s", wxGetApp().get_bbl_client_version());
|
||||
|
||||
// set the frame icon
|
||||
|
||||
|
||||
@@ -276,7 +276,7 @@ wxWebView* WebView::CreateWebView(wxWindow * parent, wxString const & url)
|
||||
#ifdef __WIN32__
|
||||
webView->SetUserAgent(wxString::Format("Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.52 BBL-Slicer/v%s (%s) BBL-Language/%s",
|
||||
SLIC3R_VERSION, Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light", language_code.mb_str()));
|
||||
Slic3r::GUI::wxGetApp().get_bbl_client_version(), Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light", language_code.mb_str()));
|
||||
webView->Create(parent, wxID_ANY, url2, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
||||
// We register the wxfs:// protocol for testing purposes
|
||||
webView->RegisterHandler(wxSharedPtr<wxWebViewHandler>(new wxWebViewArchiveHandler("bbl")));
|
||||
@@ -294,7 +294,7 @@ wxWebView* WebView::CreateWebView(wxWindow * parent, wxString const & url)
|
||||
}
|
||||
webView->Create(parent, wxID_ANY, url2, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
||||
webView->SetUserAgent(wxString::Format("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) BBL-Slicer/v%s (%s) BBL-Language/%s",
|
||||
SLIC3R_VERSION, Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light", language_code.mb_str()));
|
||||
Slic3r::GUI::wxGetApp().get_bbl_client_version(), Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light", language_code.mb_str()));
|
||||
#endif
|
||||
#ifdef __WXMAC__
|
||||
WKWebView * wkWebView = (WKWebView *) webView->GetNativeBackend();
|
||||
@@ -399,7 +399,7 @@ void WebView::RecreateAll()
|
||||
language_code = language_code.ToStdString();
|
||||
for (auto webView : g_webviews) {
|
||||
webView->SetUserAgent(wxString::Format("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) BBL-Slicer/v%s (%s) BBL-Language/%s",
|
||||
SLIC3R_VERSION, dark ? "dark" : "light", language_code.mb_str()));
|
||||
Slic3r::GUI::wxGetApp().get_bbl_client_version(), dark ? "dark" : "light", language_code.mb_str()));
|
||||
webView->Reload();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user