Connect login: updating also user script when refreshed access token arives, this should fix passing old token on page reload

This commit is contained in:
Jan Bařtipán 2024-08-22 13:39:12 +02:00 committed by Lukas Matena
parent f9164da380
commit 3d130ceb76
6 changed files with 102 additions and 13 deletions

View File

@ -332,6 +332,8 @@ set(SLIC3R_GUI_SOURCES
Utils/Http.hpp
Utils/FixModelByWin10.cpp
Utils/FixModelByWin10.hpp
Utils/Jwt.cpp
Utils/Jwt.hpp
Utils/Moonraker.cpp
Utils/Moonraker.hpp
Utils/OctoPrint.cpp

View File

@ -475,8 +475,7 @@ void UserAccountCommunication::enqueue_refresh()
BOOST_LOG_TRIVIAL(error) << "Connect Printers endpoint connection failed - Not Logged in.";
return;
}
if (m_session->is_enqueued(UserAccountActionID::USER_ACCOUNT_ACTION_REFRESH_TOKEN))
{
if (m_session->is_enqueued(UserAccountActionID::USER_ACCOUNT_ACTION_REFRESH_TOKEN)) {
BOOST_LOG_TRIVIAL(debug) << "User Account: Token refresh already enqueued, skipping...";
return;
}

View File

@ -2,6 +2,7 @@
#include "GUI_App.hpp"
#include "format.hpp"
#include "../Utils/Http.hpp"
#include "../Utils/Jwt.hpp"
#include "I18N.hpp"
#include <boost/log/trivial.hpp>
@ -53,8 +54,17 @@ void UserActionGetWithEvent::perform(wxEvtHandler* evt_handler, const std::strin
{
std::string url = m_url + input;
auto http = Http::get(std::move(url));
if (!access_token.empty())
if (!access_token.empty()) {
http.header("Authorization", "Bearer " + access_token);
#ifndef _NDEBUG
// In debug mode, also verify the token expiration
// This is here to help with "dev" accounts with shorten (sort of faked) expiration time
// The /api/v1/me will accept these tokens even if these are fake-marked as expired
if (!Utils::verify_exp(access_token)) {
fail_callback("Token Expired");
}
#endif
}
http.on_error([evt_handler, fail_callback, action_name = &m_action_name, fail_evt_type = m_fail_evt_type](std::string body, std::string error, unsigned status) {
if (fail_callback)
fail_callback(body);

View File

@ -540,7 +540,7 @@ void ConnectRequestHandler::handle_message(const std::string& message)
void ConnectRequestHandler::on_connect_action_error(const std::string &message_data)
{
BOOST_LOG_TRIVIAL(error) << "WebKit runtime error: " << message_data;
BOOST_LOG_TRIVIAL(error) << "WebView runtime error: " << message_data;
}
void ConnectRequestHandler::resend_config()
@ -550,7 +550,7 @@ void ConnectRequestHandler::resend_config()
void ConnectRequestHandler::on_connect_action_log(const std::string& message_data)
{
BOOST_LOG_TRIVIAL(info) << "WebKit log: " << message_data;
BOOST_LOG_TRIVIAL(info) << "WebView log: " << message_data;
}
void ConnectRequestHandler::on_connect_action_request_config(const std::string& message_data)
@ -647,8 +647,9 @@ wxString ConnectWebViewPanel::get_login_script(bool refresh)
R"(
if (window._prusaSlicer_initLogin !== undefined) {
console.log('Refreshing login');
_prusaSlicer.postMessage({action: 'LOG', message: 'Refreshing login'});
_prusaSlicer_initLogin('%s');
if (window._prusaSlicer !== undefined)
_prusaSlicer.postMessage({action: 'LOG', message: 'Refreshing login'});
_prusaSlicer_initLogin('%s', 'refresh');
} else {
console.log('Refreshing login skipped as no _prusaSlicer_initLogin defined (yet?)');
if (window._prusaSlicer === undefined) {
@ -660,7 +661,11 @@ wxString ConnectWebViewPanel::get_login_script(bool refresh)
)"
:
R"(
function _prusaSlicer_log(msg) { console.log(msg); _prusaSlicer.postMessage({action: 'LOG', message: msg}); }
function _prusaSlicer_log(msg) {
console.log(msg);
if (window._prusaSlicer !== undefined)
_prusaSlicer.postMessage({action: 'LOG', message: msg});
}
function _prusaSlicer_errorHandler(err) {
const msg = {
action: 'ERROR',
@ -677,7 +682,7 @@ wxString ConnectWebViewPanel::get_login_script(bool refresh)
});
}
async function _prusaSlicer_initLogin(token) {
async function _prusaSlicer_initLogin(token, reason) {
const parts = token.split('.');
const claims = JSON.parse(atob(parts[1]));
const now = new Date().getTime() / 1000;
@ -694,10 +699,10 @@ wxString ConnectWebViewPanel::get_login_script(bool refresh)
let error = false;
try {
_prusaSlicer_log('Slicer Login request');
_prusaSlicer_log('Slicer Login request (' + reason + ') ' + token.substring(token.length - 8));
let resp = await fetch('/slicer/login', {method: 'POST', headers: {Authorization: 'Bearer ' + token}});
let body = await resp.text();
_prusaSlicer_log('Slicer Login resp ' + resp.status + ' body: ' + body);
_prusaSlicer_log('Slicer Login resp ' + resp.status + ' (' +reason + ' ' + token.substring(token.length - 8) + ') body: ' + body);
if (resp.status >= 500 || resp.status == 408) {
retry = true;
} else {
@ -721,7 +726,7 @@ wxString ConnectWebViewPanel::get_login_script(bool refresh)
}
if (window._prusaSlicer_initialLoad === undefined) {
console.log('Initial login');
_prusaSlicer_initLogin('%s');
_prusaSlicer_initLogin('%s', 'init-load');
window._prusaSlicer_initialLoad = true;
}
)",
@ -743,7 +748,12 @@ void ConnectWebViewPanel::on_user_token(UserAccountSuccessEvent& e)
e.Skip();
auto access_token = wxGetApp().plater()->get_user_account()->get_access_token();
assert(!access_token.empty());
wxString javascript = get_login_script(true);
wxString javascript = get_login_script(false);
m_browser->RemoveAllUserScripts();
m_browser->AddUserScript(javascript);
javascript = get_login_script(true);
//m_browser->AddUserScript(javascript, wxWEBVIEW_INJECT_AT_DOCUMENT_END);
BOOST_LOG_TRIVIAL(debug) << "RunScript " << javascript << "\n";
m_browser->RunScriptAsync(javascript);

58
src/slic3r/Utils/Jwt.cpp Normal file
View File

@ -0,0 +1,58 @@
#include "Jwt.hpp"
#include <ctime>
#include <sstream>
#include <tuple>
#include <boost/beast/core/detail/base64.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
namespace Slic3r::Utils {
bool verify_exp(const std::string& token)
{
size_t payload_start = token.find('.');
if (payload_start == std::string::npos)
return false;
payload_start += 1; // payload starts after dot
const size_t payload_end = token.find('.', payload_start);
if (payload_end == std::string::npos)
return false;
size_t encoded_length = payload_end - payload_start;
size_t decoded_length = boost::beast::detail::base64::decoded_size(encoded_length);
auto json_b64 = token.substr(payload_start, encoded_length);
std::replace(json_b64.begin(), json_b64.end(), '-', '+');
std::replace(json_b64.begin(), json_b64.end(), '_', '/');
size_t padding = encoded_length % 4;
encoded_length += padding;
while (padding--) json_b64 += '=';
std::string json;
json.resize(decoded_length + 2);
size_t read_bytes, written_bytes;
std::tie(written_bytes, read_bytes) = boost::beast::detail::base64::decode(json.data(), json_b64.data(), json_b64.length());
json.resize(written_bytes);
if (written_bytes == 0)
return false;
namespace pt = boost::property_tree;
pt::ptree payload;
std::istringstream iss(json);
pt::json_parser::read_json(iss, payload);
auto exp_opt = payload.get_optional<double>("exp");
if (!exp_opt)
return false;
auto now = time(nullptr);
return exp_opt.get() > now;
}
}

10
src/slic3r/Utils/Jwt.hpp Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <string>
namespace Slic3r::Utils {
bool verify_exp(const std::string& token);
}