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:
SoftFever
2026-04-30 16:12:08 +08:00
committed by GitHub
parent dae0694e90
commit d78ad8c44e
14 changed files with 162 additions and 8 deletions

View File

@@ -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)