From 23442442d86862536acab5a7deca28c1c703b11e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Tue, 31 Aug 2021 00:55:56 +0200 Subject: [PATCH] GH-3392 fix a bunch of bugs and implement STS error states --- launcher/CMakeLists.txt | 2 + launcher/minecraft/auth/flows/AuthContext.cpp | 257 ++++++++++++------ launcher/minecraft/auth/flows/AuthContext.h | 27 +- launcher/minecraft/auth/flows/AuthRequest.cpp | 121 +++++++++ launcher/minecraft/auth/flows/AuthRequest.h | 65 +++++ launcher/minecraft/auth/flows/MSAHelper.txt | 51 ---- 6 files changed, 377 insertions(+), 146 deletions(-) create mode 100644 launcher/minecraft/auth/flows/AuthRequest.cpp create mode 100644 launcher/minecraft/auth/flows/AuthRequest.h delete mode 100644 launcher/minecraft/auth/flows/MSAHelper.txt diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 81740adbc..7a5e41734 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -215,6 +215,8 @@ set(MINECRAFT_SOURCES minecraft/auth/MinecraftAccount.cpp minecraft/auth/flows/AuthContext.h minecraft/auth/flows/AuthContext.cpp + minecraft/auth/flows/AuthRequest.h + minecraft/auth/flows/AuthRequest.cpp minecraft/auth/flows/MSAInteractive.h minecraft/auth/flows/MSAInteractive.cpp diff --git a/launcher/minecraft/auth/flows/AuthContext.cpp b/launcher/minecraft/auth/flows/AuthContext.cpp index 5d7d858d4..1203dc5f1 100644 --- a/launcher/minecraft/auth/flows/AuthContext.cpp +++ b/launcher/minecraft/auth/flows/AuthContext.cpp @@ -16,20 +16,21 @@ #include "AuthContext.h" #include "katabasis/Globals.h" -#include "katabasis/Requestor.h" +#include "AuthRequest.h" #ifdef EMBED_SECRETS #include "Secrets.h" #endif +#include "Env.h" + using OAuth2 = Katabasis::OAuth2; -using Requestor = Katabasis::Requestor; +using Requestor = AuthRequest; using Activity = Katabasis::Activity; AuthContext::AuthContext(AccountData * data, QObject *parent) : AccountTask(data, parent) { - mgr = new QNetworkAccessManager(this); } void AuthContext::beginActivity(Activity activity) { @@ -63,7 +64,7 @@ void AuthContext::initMSA() { opts.accessTokenUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"; opts.listenerPorts = {28562, 28563, 28564, 28565, 28566}; - m_oauth2 = new OAuth2(opts, m_data->msaToken, this, mgr); + m_oauth2 = new OAuth2(opts, m_data->msaToken, this, &ENV.qnam()); m_oauth2->setGrantFlow(Katabasis::OAuth2::GrantFlowDevice); connect(m_oauth2, &OAuth2::linkingFailed, this, &AuthContext::onOAuthLinkingFailed); @@ -161,7 +162,7 @@ void AuthContext::doUserAuth() { QNetworkRequest request = QNetworkRequest(QUrl("https://user.auth.xboxlive.com/user/authenticate")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Accept", "application/json"); - auto *requestor = new Requestor(mgr, m_oauth2, this); + auto *requestor = new Requestor(this); connect(requestor, &Requestor::finished, this, &AuthContext::onUserAuthDone); requestor->post(request, xbox_auth_data.toUtf8()); qDebug() << "First layer of XBox auth ... commencing."; @@ -192,6 +193,13 @@ bool getNumber(QJsonValue value, double & out) { return true; } +bool getNumber(QJsonValue value, int64_t & out) { + if(!value.isDouble()) { + return false; + } + out = (int64_t) value.toDouble(); + return true; +} bool getBool(QJsonValue value, bool & out) { if(!value.isBool()) { @@ -292,7 +300,6 @@ bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, const char } void AuthContext::onUserAuthDone( - int requestId, QNetworkReply::NetworkError error, QByteArray replyData, QList headers @@ -349,35 +356,58 @@ void AuthContext::doSTSAuthMinecraft() { QNetworkRequest request = QNetworkRequest(QUrl("https://xsts.auth.xboxlive.com/xsts/authorize")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Accept", "application/json"); - Requestor *requestor = new Requestor(mgr, m_oauth2, this); + Requestor *requestor = new Requestor(this); connect(requestor, &Requestor::finished, this, &AuthContext::onSTSAuthMinecraftDone); requestor->post(request, xbox_auth_data.toUtf8()); qDebug() << "Getting Minecraft services STS token..."; } +void AuthContext::processSTSError(QNetworkReply::NetworkError error, QByteArray data, QList headers) { + if(error == QNetworkReply::AuthenticationRequiredError) { + QJsonParseError jsonError; + QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); + if(jsonError.error) { + qWarning() << "Cannot parse error XSTS response as JSON: " << jsonError.errorString(); + return; + } + + int64_t errorCode = -1; + auto obj = doc.object(); + if(!getNumber(obj.value("XErr"), errorCode)) { + qWarning() << "XErr is not a number"; + return; + } + stsErrors.insert(errorCode); + stsFailed = true; + } +} + + void AuthContext::onSTSAuthMinecraftDone( - int requestId, QNetworkReply::NetworkError error, QByteArray replyData, QList headers ) { +#ifndef NDEBUG + qDebug() << replyData; +#endif if (error != QNetworkReply::NoError) { qWarning() << "Reply error:" << error; - m_requestsDone ++; + processSTSError(error, replyData, headers); + failResult(m_mcAuthSucceeded); return; } Katabasis::Token temp; if(!parseXTokenResponse(replyData, temp, "STSAuthMinecraft")) { qWarning() << "Could not parse authorization response for access to mojang services..."; - m_requestsDone ++; + failResult(m_mcAuthSucceeded); return; } if(temp.extra["uhs"] != m_data->userToken.extra["uhs"]) { qWarning() << "Server has changed user hash in the reply... something is wrong. ABORTING"; - qDebug() << replyData; - m_requestsDone ++; + failResult(m_mcAuthSucceeded); return; } m_data->mojangservicesToken = temp; @@ -385,61 +415,6 @@ void AuthContext::onSTSAuthMinecraftDone( doMinecraftAuth(); } -void AuthContext::doSTSAuthGeneric() { - QString xbox_auth_template = R"XXX( -{ - "Properties": { - "SandboxId": "RETAIL", - "UserTokens": [ - "%1" - ] - }, - "RelyingParty": "http://xboxlive.com", - "TokenType": "JWT" -} -)XXX"; - auto xbox_auth_data = xbox_auth_template.arg(m_data->userToken.token); - - QNetworkRequest request = QNetworkRequest(QUrl("https://xsts.auth.xboxlive.com/xsts/authorize")); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - request.setRawHeader("Accept", "application/json"); - Requestor *requestor = new Requestor(mgr, m_oauth2, this); - connect(requestor, &Requestor::finished, this, &AuthContext::onSTSAuthGenericDone); - requestor->post(request, xbox_auth_data.toUtf8()); - qDebug() << "Getting generic STS token..."; -} - -void AuthContext::onSTSAuthGenericDone( - int requestId, - QNetworkReply::NetworkError error, - QByteArray replyData, - QList headers -) { - if (error != QNetworkReply::NoError) { - qWarning() << "Reply error:" << error; - m_requestsDone ++; - return; - } - - Katabasis::Token temp; - if(!parseXTokenResponse(replyData, temp, "STSAuthGaneric")) { - qWarning() << "Could not parse authorization response for access to xbox API..."; - m_requestsDone ++; - return; - } - - if(temp.extra["uhs"] != m_data->userToken.extra["uhs"]) { - qWarning() << "Server has changed user hash in the reply... something is wrong. ABORTING"; - qDebug() << replyData; - m_requestsDone ++; - return; - } - m_data->xboxApiToken = temp; - - doXBoxProfile(); -} - - void AuthContext::doMinecraftAuth() { QString mc_auth_template = R"XXX( { @@ -451,7 +426,7 @@ void AuthContext::doMinecraftAuth() { QNetworkRequest request = QNetworkRequest(QUrl("https://api.minecraftservices.com/authentication/login_with_xbox")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Accept", "application/json"); - Requestor *requestor = new Requestor(mgr, m_oauth2, this); + Requestor *requestor = new Requestor(this); connect(requestor, &Requestor::finished, this, &AuthContext::onMinecraftAuthDone); requestor->post(request, data.toUtf8()); qDebug() << "Getting Minecraft access token..."; @@ -498,18 +473,16 @@ bool parseMojangResponse(QByteArray & data, Katabasis::Token &output) { } void AuthContext::onMinecraftAuthDone( - int requestId, QNetworkReply::NetworkError error, QByteArray replyData, QList headers ) { - m_requestsDone ++; - if (error != QNetworkReply::NoError) { qWarning() << "Reply error:" << error; #ifndef NDEBUG qDebug() << replyData; #endif + failResult(m_mcAuthSucceeded); return; } @@ -518,11 +491,67 @@ void AuthContext::onMinecraftAuthDone( #ifndef NDEBUG qDebug() << replyData; #endif + failResult(m_mcAuthSucceeded); return; } - m_mcAuthSucceeded = true; - checkResult(); + succeedResult(m_mcAuthSucceeded); +} + +void AuthContext::doSTSAuthGeneric() { + QString xbox_auth_template = R"XXX( +{ + "Properties": { + "SandboxId": "RETAIL", + "UserTokens": [ + "%1" + ] + }, + "RelyingParty": "http://xboxlive.com", + "TokenType": "JWT" +} +)XXX"; + auto xbox_auth_data = xbox_auth_template.arg(m_data->userToken.token); + + QNetworkRequest request = QNetworkRequest(QUrl("https://xsts.auth.xboxlive.com/xsts/authorize")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + request.setRawHeader("Accept", "application/json"); + Requestor *requestor = new Requestor(this); + connect(requestor, &Requestor::finished, this, &AuthContext::onSTSAuthGenericDone); + requestor->post(request, xbox_auth_data.toUtf8()); + qDebug() << "Getting generic STS token..."; +} + +void AuthContext::onSTSAuthGenericDone( + QNetworkReply::NetworkError error, + QByteArray replyData, + QList headers +) { +#ifndef NDEBUG + qDebug() << replyData; +#endif + if (error != QNetworkReply::NoError) { + qWarning() << "Reply error:" << error; + processSTSError(error, replyData, headers); + failResult(m_xboxProfileSucceeded); + return; + } + + Katabasis::Token temp; + if(!parseXTokenResponse(replyData, temp, "STSAuthGaneric")) { + qWarning() << "Could not parse authorization response for access to xbox API..."; + failResult(m_xboxProfileSucceeded); + return; + } + + if(temp.extra["uhs"] != m_data->userToken.extra["uhs"]) { + qWarning() << "Server has changed user hash in the reply... something is wrong. ABORTING"; + failResult(m_xboxProfileSucceeded); + return; + } + m_data->xboxApiToken = temp; + + doXBoxProfile(); } void AuthContext::doXBoxProfile() { @@ -543,25 +572,23 @@ void AuthContext::doXBoxProfile() { request.setRawHeader("Accept", "application/json"); request.setRawHeader("x-xbl-contract-version", "3"); request.setRawHeader("Authorization", QString("XBL3.0 x=%1;%2").arg(m_data->userToken.extra["uhs"].toString(), m_data->xboxApiToken.token).toUtf8()); - Requestor *requestor = new Requestor(mgr, m_oauth2, this); + Requestor *requestor = new Requestor(this); connect(requestor, &Requestor::finished, this, &AuthContext::onXBoxProfileDone); requestor->get(request); qDebug() << "Getting Xbox profile..."; } void AuthContext::onXBoxProfileDone( - int requestId, QNetworkReply::NetworkError error, QByteArray replyData, QList headers ) { - m_requestsDone ++; - if (error != QNetworkReply::NoError) { qWarning() << "Reply error:" << error; #ifndef NDEBUG qDebug() << replyData; #endif + failResult(m_xboxProfileSucceeded); return; } @@ -569,7 +596,18 @@ void AuthContext::onXBoxProfileDone( qDebug() << "XBox profile: " << replyData; #endif - m_xboxProfileSucceeded = true; + succeedResult(m_xboxProfileSucceeded); +} + +void AuthContext::succeedResult(bool& flag) { + m_requestsDone ++; + flag = true; + checkResult(); +} + +void AuthContext::failResult(bool& flag) { + m_requestsDone ++; + flag = false; checkResult(); } @@ -584,7 +622,42 @@ void AuthContext::checkResult() { } else { finishActivity(); - changeState(STATE_FAILED_HARD, tr("XBox and/or Mojang authentication steps did not succeed")); + if(stsFailed) { + if(stsErrors.contains(2148916233)) { + changeState( + STATE_FAILED_HARD, + tr("This Microsoft account does not have an XBox Live profile. Buy the game on %1 first.") + .arg("minecraft.net") + ); + } + else if (stsErrors.contains(2148916235)){ + // NOTE: this is the Grulovia error + changeState( + STATE_FAILED_HARD, + tr("XBox Live is not available in your country. You've been blocked.") + ); + } + else if (stsErrors.contains(2148916238)){ + changeState( + STATE_FAILED_HARD, + tr("This Microsoft account is underaged and is not linked to a family.\n\nPlease set up your account according to %1.") + .arg("help.minecraft.net") + ); + } + else { + QStringList errorList; + for(auto & error: stsErrors) { + errorList.append(QString::number(error)); + } + changeState( + STATE_FAILED_HARD, + tr("XSTS authentication ended with unrecognized error(s):\n\n%1").arg(errorList.join("\n")) + ); + } + } + else { + changeState(STATE_FAILED_HARD, tr("XBox and/or Mojang authentication steps did not succeed")); + } } } @@ -678,13 +751,19 @@ void AuthContext::doMinecraftProfile() { // request.setRawHeader("Accept", "application/json"); request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8()); - Requestor *requestor = new Requestor(mgr, m_oauth2, this); + Requestor *requestor = new Requestor(this); connect(requestor, &Requestor::finished, this, &AuthContext::onMinecraftProfileDone); requestor->get(request); } -void AuthContext::onMinecraftProfileDone(int, QNetworkReply::NetworkError error, QByteArray data, QList headers) { +void AuthContext::onMinecraftProfileDone( + QNetworkReply::NetworkError error, + QByteArray data, + QList headers +) { +#ifndef NDEBUG qDebug() << data; +#endif if (error == QNetworkReply::ContentNotFoundError) { m_data->minecraftProfile = MinecraftProfile(); finishActivity(); @@ -720,7 +799,7 @@ void AuthContext::doMigrationEligibilityCheck() { request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8()); - Requestor *requestor = new Requestor(mgr, m_oauth2, this); + Requestor *requestor = new Requestor(this); connect(requestor, &Requestor::finished, this, &AuthContext::onMigrationEligibilityCheckDone); requestor->get(request); } @@ -755,7 +834,11 @@ bool parseRolloutResponse(QByteArray & data, bool& result) { return true; } -void AuthContext::onMigrationEligibilityCheckDone(int, QNetworkReply::NetworkError error, QByteArray data, QList headers) { +void AuthContext::onMigrationEligibilityCheckDone( + QNetworkReply::NetworkError error, + QByteArray data, + QList headers +) { if (error == QNetworkReply::NoError) { parseRolloutResponse(data, m_data->canMigrateToMSA); } @@ -768,12 +851,16 @@ void AuthContext::doGetSkin() { auto url = QUrl(m_data->minecraftProfile.skin.url); QNetworkRequest request = QNetworkRequest(url); - Requestor *requestor = new Requestor(mgr, m_oauth2, this); + Requestor *requestor = new Requestor(this); connect(requestor, &Requestor::finished, this, &AuthContext::onSkinDone); requestor->get(request); } -void AuthContext::onSkinDone(int, QNetworkReply::NetworkError error, QByteArray data, QList) { +void AuthContext::onSkinDone( + QNetworkReply::NetworkError error, + QByteArray data, + QList +) { if (error == QNetworkReply::NoError) { m_data->minecraftProfile.skin.data = data; } diff --git a/launcher/minecraft/auth/flows/AuthContext.h b/launcher/minecraft/auth/flows/AuthContext.h index 7bf696236..dc7552aca 100644 --- a/launcher/minecraft/auth/flows/AuthContext.h +++ b/launcher/minecraft/auth/flows/AuthContext.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -48,27 +49,31 @@ protected: void initMojang(); void doUserAuth(); - Q_SLOT void onUserAuthDone(int, QNetworkReply::NetworkError, QByteArray, QList); + Q_SLOT void onUserAuthDone(QNetworkReply::NetworkError, QByteArray, QList); + + void processSTSError(QNetworkReply::NetworkError, QByteArray, QList); void doSTSAuthMinecraft(); - Q_SLOT void onSTSAuthMinecraftDone(int, QNetworkReply::NetworkError, QByteArray, QList); + Q_SLOT void onSTSAuthMinecraftDone(QNetworkReply::NetworkError, QByteArray, QList); void doMinecraftAuth(); - Q_SLOT void onMinecraftAuthDone(int, QNetworkReply::NetworkError, QByteArray, QList); + Q_SLOT void onMinecraftAuthDone(QNetworkReply::NetworkError, QByteArray, QList); void doSTSAuthGeneric(); - Q_SLOT void onSTSAuthGenericDone(int, QNetworkReply::NetworkError, QByteArray, QList); + Q_SLOT void onSTSAuthGenericDone(QNetworkReply::NetworkError, QByteArray, QList); void doXBoxProfile(); - Q_SLOT void onXBoxProfileDone(int, QNetworkReply::NetworkError, QByteArray, QList); + Q_SLOT void onXBoxProfileDone(QNetworkReply::NetworkError, QByteArray, QList); void doMinecraftProfile(); - Q_SLOT void onMinecraftProfileDone(int, QNetworkReply::NetworkError, QByteArray, QList); + Q_SLOT void onMinecraftProfileDone(QNetworkReply::NetworkError, QByteArray, QList); void doMigrationEligibilityCheck(); - Q_SLOT void onMigrationEligibilityCheckDone(int, QNetworkReply::NetworkError, QByteArray, QList); + Q_SLOT void onMigrationEligibilityCheckDone(QNetworkReply::NetworkError, QByteArray, QList); void doGetSkin(); - Q_SLOT void onSkinDone(int, QNetworkReply::NetworkError, QByteArray, QList); + Q_SLOT void onSkinDone(QNetworkReply::NetworkError, QByteArray, QList); + void failResult(bool & flag); + void succeedResult(bool & flag); void checkResult(); protected: @@ -83,6 +88,10 @@ protected: int m_requestsDone = 0; bool m_xboxProfileSucceeded = false; bool m_mcAuthSucceeded = false; + + QSet stsErrors; + bool stsFailed = false; + Katabasis::Activity m_activity = Katabasis::Activity::Idle; enum class AuthStage { Initial, @@ -95,6 +104,4 @@ protected: } m_stage = AuthStage::Initial; void setStage(AuthStage stage); - - QNetworkAccessManager *mgr = nullptr; }; diff --git a/launcher/minecraft/auth/flows/AuthRequest.cpp b/launcher/minecraft/auth/flows/AuthRequest.cpp new file mode 100644 index 000000000..77558fd3b --- /dev/null +++ b/launcher/minecraft/auth/flows/AuthRequest.cpp @@ -0,0 +1,121 @@ +#include + +#include +#include +#include +#include + +#include "AuthRequest.h" +#include "katabasis/Globals.h" +#include "Env.h" + +AuthRequest::AuthRequest(QObject *parent): QObject(parent) { +} + +AuthRequest::~AuthRequest() { +} + +void AuthRequest::get(const QNetworkRequest &req, int timeout/* = 60*1000*/) { + setup(req, QNetworkAccessManager::GetOperation); + reply_ = ENV.qnam().get(request_); + status_ = Requesting; + timedReplies_.add(new Katabasis::Reply(reply_, timeout)); + connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError))); + connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished())); + connect(reply_, &QNetworkReply::sslErrors, this, &AuthRequest::onSslErrors); +} + +void AuthRequest::post(const QNetworkRequest &req, const QByteArray &data, int timeout/* = 60*1000*/) { + setup(req, QNetworkAccessManager::PostOperation); + data_ = data; + status_ = Requesting; + reply_ = ENV.qnam().post(request_, data_); + timedReplies_.add(new Katabasis::Reply(reply_, timeout)); + connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError))); + connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished())); + connect(reply_, &QNetworkReply::sslErrors, this, &AuthRequest::onSslErrors); + connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64))); +} + +void AuthRequest::onRequestFinished() { + if (status_ == Idle) { + return; + } + if (reply_ != qobject_cast(sender())) { + return; + } + finish(); +} + +void AuthRequest::onRequestError(QNetworkReply::NetworkError error) { + qWarning() << "AuthRequest::onRequestError: Error" << (int)error; + if (status_ == Idle) { + return; + } + if (reply_ != qobject_cast(sender())) { + return; + } + qWarning() << "AuthRequest::onRequestError: Error string: " << reply_->errorString(); + int httpStatus = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + qWarning() << "AuthRequest::onRequestError: HTTP status" << httpStatus << reply_->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); + error_ = error; + + // QTimer::singleShot(10, this, SLOT(finish())); +} + +void AuthRequest::onSslErrors(QList errors) { + int i = 1; + for (auto error : errors) { + qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString(); + auto cert = error.certificate(); + qCritical() << "Certificate in question:\n" << cert.toText(); + i++; + } +} + +void AuthRequest::onUploadProgress(qint64 uploaded, qint64 total) { + if (status_ == Idle) { + qWarning() << "AuthRequest::onUploadProgress: No pending request"; + return; + } + if (reply_ != qobject_cast(sender())) { + return; + } + // Restart timeout because request in progress + Katabasis::Reply *o2Reply = timedReplies_.find(reply_); + if(o2Reply) { + o2Reply->start(); + } + emit uploadProgress(uploaded, total); +} + +void AuthRequest::setup(const QNetworkRequest &req, QNetworkAccessManager::Operation operation, const QByteArray &verb) { + request_ = req; + operation_ = operation; + url_ = req.url(); + + QUrl url = url_; + request_.setUrl(url); + + if (!verb.isEmpty()) { + request_.setRawHeader(Katabasis::HTTP_HTTP_HEADER, verb); + } + + status_ = Requesting; + error_ = QNetworkReply::NoError; +} + +void AuthRequest::finish() { + QByteArray data; + if (status_ == Idle) { + qWarning() << "AuthRequest::finish: No pending request"; + return; + } + data = reply_->readAll(); + status_ = Idle; + timedReplies_.remove(reply_); + reply_->disconnect(this); + reply_->deleteLater(); + QList headers = reply_->rawHeaderPairs(); + emit finished(error_, data, headers); +} diff --git a/launcher/minecraft/auth/flows/AuthRequest.h b/launcher/minecraft/auth/flows/AuthRequest.h new file mode 100644 index 000000000..6a45a0bde --- /dev/null +++ b/launcher/minecraft/auth/flows/AuthRequest.h @@ -0,0 +1,65 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include "katabasis/Reply.h" + +/// Makes authentication requests. +class AuthRequest: public QObject { + Q_OBJECT + +public: + explicit AuthRequest(QObject *parent = 0); + ~AuthRequest(); + +public slots: + void get(const QNetworkRequest &req, int timeout = 60*1000); + void post(const QNetworkRequest &req, const QByteArray &data, int timeout = 60*1000); + + +signals: + + /// Emitted when a request has been completed or failed. + void finished(QNetworkReply::NetworkError error, QByteArray data, QList headers); + + /// Emitted when an upload has progressed. + void uploadProgress(qint64 bytesSent, qint64 bytesTotal); + +protected slots: + + /// Handle request finished. + void onRequestFinished(); + + /// Handle request error. + void onRequestError(QNetworkReply::NetworkError error); + + /// Handle ssl errors. + void onSslErrors(QList errors); + + /// Finish the request, emit finished() signal. + void finish(); + + /// Handle upload progress. + void onUploadProgress(qint64 uploaded, qint64 total); + +protected: + void setup(const QNetworkRequest &request, QNetworkAccessManager::Operation operation, const QByteArray &verb = QByteArray()); + + enum Status { + Idle, Requesting, ReRequesting + }; + + QNetworkRequest request_; + QByteArray data_; + QNetworkReply *reply_; + Status status_; + QNetworkAccessManager::Operation operation_; + QUrl url_; + Katabasis::ReplyList timedReplies_; + QNetworkReply::NetworkError error_; +}; diff --git a/launcher/minecraft/auth/flows/MSAHelper.txt b/launcher/minecraft/auth/flows/MSAHelper.txt deleted file mode 100644 index dfaec3749..000000000 --- a/launcher/minecraft/auth/flows/MSAHelper.txt +++ /dev/null @@ -1,51 +0,0 @@ -class Helper : public QObject { - Q_OBJECT - -public: - Helper(MSAFlows * context) : QObject(), context_(context), msg_(QString()) { - QFile tokenCache("usercache.dat"); - if(tokenCache.open(QIODevice::ReadOnly)) { - context_->resumeFromState(tokenCache.readAll()); - } - } - -public slots: - void run() { - connect(context_, &MSAFlows::activityChanged, this, &Helper::onActivityChanged); - context_->silentSignIn(); - } - - void onFailed() { - qDebug() << "Login failed"; - } - - void onActivityChanged(Katabasis::Activity activity) { - if(activity == Katabasis::Activity::Idle) { - switch(context_->validity()) { - case Katabasis::Validity::None: { - // account is gone, remove it. - QFile::remove("usercache.dat"); - } - break; - case Katabasis::Validity::Assumed: { - // this is basically a soft-failed refresh. do nothing. - } - break; - case Katabasis::Validity::Certain: { - // stuff got refreshed / signed in. Save. - auto data = context_->saveState(); - QSaveFile tokenCache("usercache.dat"); - if(tokenCache.open(QIODevice::WriteOnly)) { - tokenCache.write(context_->saveState()); - tokenCache.commit(); - } - } - break; - } - } - } - -private: - MSAFlows *context_; - QString msg_; -};