From cf75b6427d8212bd0a24d3beca46e247dc9dcbd6 Mon Sep 17 00:00:00 2001 From: Evan Goode <mail@evangoo.de> Date: Fri, 2 Dec 2022 23:00:25 -0500 Subject: [PATCH 1/6] 'Custom Yggdrasil' AccountType Signed-off-by: Evan Goode <mail@evangoo.de> --- buildconfig/BuildConfig.h | 8 +- launcher/CMakeLists.txt | 5 + launcher/minecraft/MinecraftInstance.cpp | 14 ++ launcher/minecraft/MinecraftInstance.h | 2 + launcher/minecraft/auth/AccountData.cpp | 63 +++++++- launcher/minecraft/auth/AccountData.h | 12 ++ launcher/minecraft/auth/AccountList.cpp | 4 +- launcher/minecraft/auth/AuthSession.h | 7 + launcher/minecraft/auth/MinecraftAccount.cpp | 39 +++++ launcher/minecraft/auth/MinecraftAccount.h | 60 +++++++ launcher/minecraft/auth/Yggdrasil.cpp | 7 +- .../minecraft/auth/flows/CustomYggdrasil.cpp | 25 +++ .../minecraft/auth/flows/CustomYggdrasil.h | 26 +++ .../auth/steps/MinecraftProfileStep.cpp | 4 +- .../auth/steps/MinecraftProfileStepMojang.cpp | 9 +- .../minecraft/launch/DirectJavaLaunch.cpp | 2 + .../minecraft/launch/LauncherPartLaunch.cpp | 3 + launcher/minecraft/services/CapeChange.cpp | 14 +- launcher/minecraft/services/CapeChange.h | 5 +- launcher/minecraft/services/SkinUpload.cpp | 9 +- launcher/minecraft/services/SkinUpload.h | 5 +- .../ui/dialogs/CustomYggdrasilLoginDialog.cpp | 148 ++++++++++++++++++ .../ui/dialogs/CustomYggdrasilLoginDialog.h | 60 +++++++ .../ui/dialogs/CustomYggdrasilLoginDialog.ui | 105 +++++++++++++ launcher/ui/dialogs/SkinUploadDialog.cpp | 4 +- launcher/ui/pages/global/AccountListPage.cpp | 30 ++++ launcher/ui/pages/global/AccountListPage.h | 1 + launcher/ui/pages/global/AccountListPage.ui | 6 + 28 files changed, 646 insertions(+), 31 deletions(-) create mode 100644 launcher/minecraft/auth/flows/CustomYggdrasil.cpp create mode 100644 launcher/minecraft/auth/flows/CustomYggdrasil.h create mode 100644 launcher/ui/dialogs/CustomYggdrasilLoginDialog.cpp create mode 100644 launcher/ui/dialogs/CustomYggdrasilLoginDialog.h create mode 100644 launcher/ui/dialogs/CustomYggdrasilLoginDialog.ui diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index 8543d724..ecb9335d 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -142,7 +142,13 @@ class Config { QString RESOURCE_BASE = "https://resources.download.minecraft.net/"; QString LIBRARY_BASE = "https://libraries.minecraft.net/"; - QString AUTH_BASE = "https://authserver.mojang.com/"; + + // Minecraft expects these without trailing slashes, best to keep that format everywhere + QString MOJANG_AUTH_BASE = "https://authserver.mojang.com"; + QString MOJANG_ACCOUNT_BASE = "https://api.mojang.com"; + QString MOJANG_SESSION_BASE = "https://sessionserver.mojang.com"; + QString MOJANG_SERVICES_BASE = "https://api.minecraftservices.com"; + QString IMGUR_BASE_URL = "https://api.imgur.com/3/"; QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index ce2771a4..9eac6a7a 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -212,6 +212,8 @@ set(MINECRAFT_SOURCES minecraft/auth/flows/AuthFlow.cpp minecraft/auth/flows/AuthFlow.h + minecraft/auth/flows/CustomYggdrasil.cpp + minecraft/auth/flows/CustomYggdrasil.h minecraft/auth/flows/Mojang.cpp minecraft/auth/flows/Mojang.h minecraft/auth/flows/MSA.cpp @@ -914,6 +916,8 @@ SET(LAUNCHER_SOURCES ui/dialogs/ImportResourceDialog.h ui/dialogs/LoginDialog.cpp ui/dialogs/LoginDialog.h + ui/dialogs/CustomYggdrasilLoginDialog.cpp + ui/dialogs/CustomYggdrasilLoginDialog.h ui/dialogs/MSALoginDialog.cpp ui/dialogs/MSALoginDialog.h ui/dialogs/OfflineLoginDialog.cpp @@ -1061,6 +1065,7 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/OfflineLoginDialog.ui ui/dialogs/AboutDialog.ui ui/dialogs/LoginDialog.ui + ui/dialogs/CustomYggdrasilLoginDialog.ui ui/dialogs/EditAccountDialog.ui ui/dialogs/ReviewMessageBox.ui ui/dialogs/ScrollMessageBox.ui diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index f8ed5214..f6a7d111 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -462,6 +462,20 @@ QString MinecraftInstance::getLauncher() return "standard"; } +QStringList MinecraftInstance::processAuthArgs(AuthSessionPtr session) const +{ + QStringList args; + if(session->uses_custom_api_servers) + { + args << "-Dminecraft.api.env=custom"; + args << "-Dminecraft.api.auth.host=" + session->auth_server_url; + args << "-Dminecraft.api.account.host=" + session->account_server_url; + args << "-Dminecraft.api.session.host=" + session->session_server_url; + args << "-Dminecraft.api.services.host=" + session->services_server_url; + } + return args; +} + QMap<QString, QString> MinecraftInstance::getVariables() { QMap<QString, QString> out; diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 068b3008..cba0a612 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -158,6 +158,8 @@ public: // FIXME: remove virtual QString getMainClass() const; + virtual QStringList processAuthArgs(AuthSessionPtr account) const; + // FIXME: remove virtual QStringList processMinecraftArgs(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) const; diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp index 44f7e256..1f32efd4 100644 --- a/launcher/minecraft/auth/AccountData.cpp +++ b/launcher/minecraft/auth/AccountData.cpp @@ -34,6 +34,8 @@ */ #include "AccountData.h" +#include "BuildConfig.h" + #include <QJsonDocument> #include <QJsonObject> #include <QJsonArray> @@ -350,6 +352,8 @@ bool AccountData::resumeStateFromV3(QJsonObject data) { type = AccountType::MSA; } else if (typeS == "Mojang") { type = AccountType::Mojang; + } else if (typeS == "CustomYggdrasil") { + type = AccountType::CustomYggdrasil; } else if (typeS == "Offline") { type = AccountType::Offline; } else { @@ -362,6 +366,13 @@ bool AccountData::resumeStateFromV3(QJsonObject data) { canMigrateToMSA = data.value("canMigrateToMSA").toBool(false); } + if(type == AccountType::CustomYggdrasil) { + customAuthServerUrl = data.value("customAuthServerUrl").toString(); + customAccountServerUrl = data.value("customAccountServerUrl").toString(); + customSessionServerUrl = data.value("customSessionServerUrl").toString(); + customServicesServerUrl = data.value("customServicesServerUrl").toString(); + } + if(type == AccountType::MSA) { auto clientIDV = data.value("msa-client-id"); if (clientIDV.isString()) { @@ -406,6 +417,13 @@ QJsonObject AccountData::saveState() const { tokenToJSONV3(output, xboxApiToken, "xrp-main"); tokenToJSONV3(output, mojangservicesToken, "xrp-mc"); } + else if (type == AccountType::CustomYggdrasil) { + output["type"] = "CustomYggdrasil"; + output["customAuthServerUrl"] = customAuthServerUrl; + output["customAccountServerUrl"] = customAccountServerUrl; + output["customSessionServerUrl"] = customSessionServerUrl; + output["customServicesServerUrl"] = customServicesServerUrl; + } else if (type == AccountType::Offline) { output["type"] = "Offline"; } @@ -416,6 +434,42 @@ QJsonObject AccountData::saveState() const { return output; } +bool AccountData::usesCustomApiServers() const { + return type == AccountType::CustomYggdrasil; +} + +QString AccountData::authServerUrl() const { + if(usesCustomApiServers()) { + return customAuthServerUrl; + } else { + return BuildConfig.MOJANG_AUTH_BASE; + } +} + +QString AccountData::accountServerUrl() const { + if(usesCustomApiServers()) { + return customAccountServerUrl; + } else { + return BuildConfig.MOJANG_ACCOUNT_BASE; + } +} + +QString AccountData::sessionServerUrl() const { + if(usesCustomApiServers()) { + return customSessionServerUrl; + } else { + return BuildConfig.MOJANG_SESSION_BASE; + } +} + +QString AccountData::servicesServerUrl() const { + if(usesCustomApiServers()) { + return customServicesServerUrl; + } else { + return BuildConfig.MOJANG_SERVICES_BASE; + } +} + QString AccountData::userName() const { if(type == AccountType::MSA) { return QString(); @@ -428,14 +482,14 @@ QString AccountData::accessToken() const { } QString AccountData::clientToken() const { - if(type != AccountType::Mojang) { + if(type != AccountType::Mojang && type != AccountType::CustomYggdrasil) { return QString(); } return yggdrasilToken.extra["clientToken"].toString(); } void AccountData::setClientToken(QString clientToken) { - if(type != AccountType::Mojang) { + if(type != AccountType::Mojang && type != AccountType::CustomYggdrasil) { return; } yggdrasilToken.extra["clientToken"] = clientToken; @@ -449,7 +503,7 @@ void AccountData::generateClientTokenIfMissing() { } void AccountData::invalidateClientToken() { - if(type != AccountType::Mojang) { + if(type != AccountType::Mojang && type != AccountType::CustomYggdrasil) { return; } yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{-}]")); @@ -473,6 +527,9 @@ QString AccountData::accountDisplayString() const { case AccountType::Mojang: { return userName(); } + case AccountType::CustomYggdrasil: { + return userName(); + } case AccountType::Offline: { return QObject::tr("<Offline>"); } diff --git a/launcher/minecraft/auth/AccountData.h b/launcher/minecraft/auth/AccountData.h index 092e1691..0d7c01df 100644 --- a/launcher/minecraft/auth/AccountData.h +++ b/launcher/minecraft/auth/AccountData.h @@ -74,6 +74,7 @@ struct MinecraftProfile { enum class AccountType { MSA, Mojang, + CustomYggdrasil, Offline }; @@ -93,6 +94,12 @@ struct AccountData { bool resumeStateFromV2(QJsonObject data); bool resumeStateFromV3(QJsonObject data); + bool usesCustomApiServers() const; + QString authServerUrl() const; + QString accountServerUrl() const; + QString sessionServerUrl() const; + QString servicesServerUrl() const; + //! userName for Mojang accounts, gamertag for MSA QString accountDisplayString() const; @@ -117,6 +124,11 @@ struct AccountData { bool legacy = false; bool canMigrateToMSA = false; + QString customAuthServerUrl; + QString customAccountServerUrl; + QString customSessionServerUrl; + QString customServicesServerUrl; + QString msaClientID; Katabasis::Token msaToken; Katabasis::Token userToken; diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index 9e2fd111..edbf2298 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -297,9 +297,7 @@ QVariant AccountList::data(const QModelIndex &index, int role) const return account->accountDisplayString(); case TypeColumn: { - auto typeStr = account->typeString(); - typeStr[0] = typeStr[0].toUpper(); - return typeStr; + return account->typeDisplayName(); } case StatusColumn: { diff --git a/launcher/minecraft/auth/AuthSession.h b/launcher/minecraft/auth/AuthSession.h index a75df506..a7a9503b 100644 --- a/launcher/minecraft/auth/AuthSession.h +++ b/launcher/minecraft/auth/AuthSession.h @@ -26,6 +26,13 @@ struct AuthSession GoneOrMigrated } status = Undetermined; + // API URLs + QString auth_server_url; + QString account_server_url; + QString session_server_url; + QString services_server_url; + bool uses_custom_api_servers = false; + // client token QString client_token; // account user name diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index 3b050ac0..09a617b1 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -50,6 +50,7 @@ #include "flows/MSA.h" #include "flows/Mojang.h" +#include "flows/CustomYggdrasil.h" #include "flows/Offline.h" MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) { @@ -82,6 +83,26 @@ MinecraftAccountPtr MinecraftAccount::createFromUsername(const QString &username return account; } +MinecraftAccountPtr MinecraftAccount::createFromUsernameCustomYggdrasil( + const QString &username, + const QString &customAuthServerUrl, + const QString &customAccountServerUrl, + const QString &customSessionServerUrl, + const QString &customServicesServerUrl +) +{ + MinecraftAccountPtr account = new MinecraftAccount(); + account->data.type = AccountType::CustomYggdrasil; + account->data.yggdrasilToken.extra["userName"] = username; + account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); + + account->data.customAuthServerUrl = customAuthServerUrl; + account->data.customAccountServerUrl = customAccountServerUrl; + account->data.customSessionServerUrl = customSessionServerUrl; + account->data.customServicesServerUrl = customServicesServerUrl; + return account; +} + MinecraftAccountPtr MinecraftAccount::createBlankMSA() { MinecraftAccountPtr account(new MinecraftAccount()); @@ -140,6 +161,17 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::login(QString password) { return m_currentTask; } +shared_qobject_ptr<AccountTask> MinecraftAccount::loginCustomYggdrasil(QString password) { + Q_ASSERT(m_currentTask.get() == nullptr); + + m_currentTask.reset(new CustomYggdrasilLogin(&data, password)); + connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); + connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); + connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); + emit activityChanged(true); + return m_currentTask; +} + shared_qobject_ptr<AccountTask> MinecraftAccount::loginMSA() { Q_ASSERT(m_currentTask.get() == nullptr); @@ -310,6 +342,13 @@ void MinecraftAccount::fillSession(AuthSessionPtr session) { session->session = "-"; } + + // API URLs + session->auth_server_url = data.authServerUrl(); + session->account_server_url = data.accountServerUrl(); + session->session_server_url = data.sessionServerUrl(); + session->services_server_url = data.servicesServerUrl(); + session->uses_custom_api_servers = data.usesCustomApiServers(); } void MinecraftAccount::decrementUses() diff --git a/launcher/minecraft/auth/MinecraftAccount.h b/launcher/minecraft/auth/MinecraftAccount.h index 0dcaeb53..61a5ab1d 100644 --- a/launcher/minecraft/auth/MinecraftAccount.h +++ b/launcher/minecraft/auth/MinecraftAccount.h @@ -90,6 +90,13 @@ public: /* construction */ explicit MinecraftAccount(QObject *parent = 0); static MinecraftAccountPtr createFromUsername(const QString &username); + static MinecraftAccountPtr createFromUsernameCustomYggdrasil( + const QString &username, + const QString &authServerUrl, + const QString &accountServerUrl, + const QString &sessionServerUrl, + const QString &servicesServerUrl + ); static MinecraftAccountPtr createBlankMSA(); @@ -109,6 +116,8 @@ public: /* manipulation */ */ shared_qobject_ptr<AccountTask> login(QString password); + shared_qobject_ptr<AccountTask> loginCustomYggdrasil(QString password); + shared_qobject_ptr<AccountTask> loginMSA(); shared_qobject_ptr<AccountTask> loginOffline(); @@ -122,6 +131,22 @@ public: /* queries */ return data.internalId; } + QString authServerUrl() const { + return data.authServerUrl(); + } + + QString accountServerUrl() const { + return data.accountServerUrl(); + } + + QString sessionServerUrl() const { + return data.sessionServerUrl(); + } + + QString servicesServerUrl() const { + return data.servicesServerUrl(); + } + QString accountDisplayString() const { return data.accountDisplayString(); } @@ -164,6 +189,34 @@ public: /* queries */ return data.profileId().size() != 0; } + QString typeDisplayName() const { + switch(data.type) { + case AccountType::Mojang: { + if(data.legacy) { + return "Legacy"; + } + return "Mojang"; + } + break; + case AccountType::CustomYggdrasil: { + return "Custom Yggdrasil"; + } + break; + case AccountType::MSA: { + return "Microsoft"; + } + break; + case AccountType::Offline: { + return "Offline"; + } + break; + default: { + return "Unknown"; + } + + } + } + QString typeString() const { switch(data.type) { case AccountType::Mojang: { @@ -173,6 +226,13 @@ public: /* queries */ return "mojang"; } break; + case AccountType::CustomYggdrasil: { + // This typeString gets passed to Minecraft; any Yggdrasil + // account should have the "mojang" type regardless of which + // servers are used. + return "mojang"; + } + break; case AccountType::MSA: { return "msa"; } diff --git a/launcher/minecraft/auth/Yggdrasil.cpp b/launcher/minecraft/auth/Yggdrasil.cpp index 29978411..f5ddf776 100644 --- a/launcher/minecraft/auth/Yggdrasil.cpp +++ b/launcher/minecraft/auth/Yggdrasil.cpp @@ -25,6 +25,7 @@ #include <QDebug> +#include "BuildConfig.h" #include "Application.h" Yggdrasil::Yggdrasil(AccountData *data, QObject *parent) @@ -72,6 +73,8 @@ void Yggdrasil::refresh() { QJsonObject req; req.insert("clientToken", m_data->clientToken()); req.insert("accessToken", m_data->accessToken()); + + qDebug() << "refreshing, access token is" << m_data->accessToken(); /* { auto currentProfile = m_account->currentProfile(); @@ -84,7 +87,7 @@ void Yggdrasil::refresh() { req.insert("requestUser", false); QJsonDocument doc(req); - QUrl reqUrl("https://authserver.mojang.com/refresh"); + QUrl reqUrl(m_data->authServerUrl() + "/refresh"); QByteArray requestData = doc.toJson(); sendRequest(reqUrl, requestData); @@ -129,7 +132,7 @@ void Yggdrasil::login(QString password) { QJsonDocument doc(req); - QUrl reqUrl("https://authserver.mojang.com/authenticate"); + QUrl reqUrl(m_data->authServerUrl() + "/authenticate"); QNetworkRequest netRequest(reqUrl); QByteArray requestData = doc.toJson(); diff --git a/launcher/minecraft/auth/flows/CustomYggdrasil.cpp b/launcher/minecraft/auth/flows/CustomYggdrasil.cpp new file mode 100644 index 00000000..a8ee6f3d --- /dev/null +++ b/launcher/minecraft/auth/flows/CustomYggdrasil.cpp @@ -0,0 +1,25 @@ +#include "CustomYggdrasil.h" + +#include "minecraft/auth/steps/YggdrasilStep.h" +#include "minecraft/auth/steps/MinecraftProfileStepMojang.h" +#include "minecraft/auth/steps/MigrationEligibilityStep.h" +#include "minecraft/auth/steps/GetSkinStep.h" + +CustomYggdrasilRefresh::CustomYggdrasilRefresh( + AccountData *data, + QObject *parent +) : AuthFlow(data, parent) { + m_steps.append(new YggdrasilStep(m_data, QString())); + m_steps.append(new MinecraftProfileStepMojang(m_data)); + m_steps.append(new GetSkinStep(m_data)); +} + +CustomYggdrasilLogin::CustomYggdrasilLogin( + AccountData *data, + QString password, + QObject *parent +): AuthFlow(data, parent), m_password(password) { + m_steps.append(new YggdrasilStep(m_data, m_password)); + m_steps.append(new MinecraftProfileStepMojang(m_data)); + m_steps.append(new GetSkinStep(m_data)); +} diff --git a/launcher/minecraft/auth/flows/CustomYggdrasil.h b/launcher/minecraft/auth/flows/CustomYggdrasil.h new file mode 100644 index 00000000..cd82bec4 --- /dev/null +++ b/launcher/minecraft/auth/flows/CustomYggdrasil.h @@ -0,0 +1,26 @@ +#pragma once +#include "AuthFlow.h" + +class CustomYggdrasilRefresh : public AuthFlow +{ + Q_OBJECT +public: + explicit CustomYggdrasilRefresh( + AccountData *data, + QObject *parent = 0 + ); +}; + +class CustomYggdrasilLogin : public AuthFlow +{ + Q_OBJECT +public: + explicit CustomYggdrasilLogin( + AccountData *data, + QString password, + QObject *parent = 0 + ); + +private: + QString m_password; +}; diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp b/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp index 6cfa7c1c..16230e04 100644 --- a/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp +++ b/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp @@ -44,7 +44,7 @@ void MinecraftProfileStep::onRequestDone( qCDebug(authCredentials()) << data; if (error == QNetworkReply::ContentNotFoundError) { // NOTE: Succeed even if we do not have a profile. This is a valid account state. - if(m_data->type == AccountType::Mojang) { + if(m_data->type == AccountType::Mojang || m_data->type == AccountType::CustomYggdrasil) { m_data->minecraftEntitlement.canPlayMinecraft = false; m_data->minecraftEntitlement.ownsMinecraft = false; } @@ -87,7 +87,7 @@ void MinecraftProfileStep::onRequestDone( return; } - if(m_data->type == AccountType::Mojang) { + if(m_data->type == AccountType::Mojang || m_data->type == AccountType::CustomYggdrasil) { auto validProfile = m_data->minecraftProfile.validity == Katabasis::Validity::Certain; m_data->minecraftEntitlement.canPlayMinecraft = validProfile; m_data->minecraftEntitlement.ownsMinecraft = validProfile; diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp index 8c378588..f8d4baed 100644 --- a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp +++ b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp @@ -3,6 +3,7 @@ #include <QNetworkRequest> #include "Logging.h" +#include "BuildConfig.h" #include "minecraft/auth/AuthRequest.h" #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" @@ -17,7 +18,6 @@ QString MinecraftProfileStepMojang::describe() { return tr("Fetching the Minecraft profile."); } - void MinecraftProfileStepMojang::perform() { if (m_data->minecraftProfile.id.isEmpty()) { emit finished(AccountTaskState::STATE_FAILED_HARD, tr("A UUID is required to get the profile.")); @@ -25,7 +25,8 @@ void MinecraftProfileStepMojang::perform() { } // use session server instead of profile due to profile endpoint being locked for locked Mojang accounts - QUrl url = QUrl("https://sessionserver.mojang.com/session/minecraft/profile/" + m_data->minecraftProfile.id); + + QUrl url = QUrl(m_data->sessionServerUrl() + "/session/minecraft/profile/" + m_data->minecraftProfile.id); QNetworkRequest req = QNetworkRequest(url); AuthRequest *request = new AuthRequest(this); connect(request, &AuthRequest::finished, this, &MinecraftProfileStepMojang::onRequestDone); @@ -47,7 +48,7 @@ void MinecraftProfileStepMojang::onRequestDone( qCDebug(authCredentials()) << data; if (error == QNetworkReply::ContentNotFoundError) { // NOTE: Succeed even if we do not have a profile. This is a valid account state. - if(m_data->type == AccountType::Mojang) { + if(m_data->type == AccountType::Mojang || m_data->type == AccountType::CustomYggdrasil) { m_data->minecraftEntitlement.canPlayMinecraft = false; m_data->minecraftEntitlement.ownsMinecraft = false; } @@ -90,7 +91,7 @@ void MinecraftProfileStepMojang::onRequestDone( return; } - if(m_data->type == AccountType::Mojang) { + if(m_data->type == AccountType::Mojang || m_data->type == AccountType::CustomYggdrasil) { auto validProfile = m_data->minecraftProfile.validity == Katabasis::Validity::Certain; m_data->minecraftEntitlement.canPlayMinecraft = validProfile; m_data->minecraftEntitlement.ownsMinecraft = validProfile; diff --git a/launcher/minecraft/launch/DirectJavaLaunch.cpp b/launcher/minecraft/launch/DirectJavaLaunch.cpp index ca55cd2e..a9a34872 100644 --- a/launcher/minecraft/launch/DirectJavaLaunch.cpp +++ b/launcher/minecraft/launch/DirectJavaLaunch.cpp @@ -39,6 +39,8 @@ void DirectJavaLaunch::executeTask() std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance); QStringList args = minecraftInstance->javaArguments(); + args.append(minecraftInstance->processAuthArgs(m_session)); + args.append("-Djava.library.path=" + minecraftInstance->getNativePath()); auto classPathEntries = minecraftInstance->getClassPath(); diff --git a/launcher/minecraft/launch/LauncherPartLaunch.cpp b/launcher/minecraft/launch/LauncherPartLaunch.cpp index 8ecf715d..79e8bcd7 100644 --- a/launcher/minecraft/launch/LauncherPartLaunch.cpp +++ b/launcher/minecraft/launch/LauncherPartLaunch.cpp @@ -110,6 +110,9 @@ void LauncherPartLaunch::executeTask() m_launchScript = minecraftInstance->createLaunchScript(m_session, m_serverToJoin); QStringList args = minecraftInstance->javaArguments(); + + args.append(minecraftInstance->processAuthArgs(m_session)); + QString allArgs = args.join(", "); emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::Launcher); diff --git a/launcher/minecraft/services/CapeChange.cpp b/launcher/minecraft/services/CapeChange.cpp index 1d5ea36d..e6e70272 100644 --- a/launcher/minecraft/services/CapeChange.cpp +++ b/launcher/minecraft/services/CapeChange.cpp @@ -40,15 +40,16 @@ #include "Application.h" -CapeChange::CapeChange(QObject *parent, QString token, QString cape) - : Task(parent), m_capeId(cape), m_token(token) +CapeChange::CapeChange(QObject *parent, MinecraftAccountPtr acct, QString cape) + : Task(parent), m_capeId(cape), m_acct(acct) { } void CapeChange::setCape(QString& cape) { - QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active")); + QNetworkRequest request(QUrl(m_acct->servicesServerUrl() + "/minecraft/profile/capes/active")); + QString token = m_acct->accessToken(); auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId); - request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); + request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toLocal8Bit()); QNetworkReply *rep = APPLICATION->network()->put(request, requestString.toUtf8()); setStatus(tr("Equipping cape")); @@ -65,9 +66,10 @@ void CapeChange::setCape(QString& cape) { } void CapeChange::clearCape() { - QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active")); + QNetworkRequest request(QUrl(m_acct->servicesServerUrl() + "/minecraft/profile/capes/active")); + QString token = m_acct->accessToken(); auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId); - request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); + request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toLocal8Bit()); QNetworkReply *rep = APPLICATION->network()->deleteResource(request); setStatus(tr("Removing cape")); diff --git a/launcher/minecraft/services/CapeChange.h b/launcher/minecraft/services/CapeChange.h index 38069f90..494b59e0 100644 --- a/launcher/minecraft/services/CapeChange.h +++ b/launcher/minecraft/services/CapeChange.h @@ -5,12 +5,13 @@ #include <memory> #include "tasks/Task.h" #include "QObjectPtr.h" +#include <minecraft/auth/MinecraftAccount.h> class CapeChange : public Task { Q_OBJECT public: - CapeChange(QObject *parent, QString token, QString capeId); + CapeChange(QObject *parent, MinecraftAccountPtr m_acct, QString capeId); virtual ~CapeChange() {} private: @@ -19,7 +20,7 @@ private: private: QString m_capeId; - QString m_token; + MinecraftAccountPtr m_acct; shared_qobject_ptr<QNetworkReply> m_reply; protected: diff --git a/launcher/minecraft/services/SkinUpload.cpp b/launcher/minecraft/services/SkinUpload.cpp index 711f8739..c7eaa51e 100644 --- a/launcher/minecraft/services/SkinUpload.cpp +++ b/launcher/minecraft/services/SkinUpload.cpp @@ -51,15 +51,16 @@ QByteArray getVariant(SkinUpload::Model model) { } } -SkinUpload::SkinUpload(QObject *parent, QString token, QByteArray skin, SkinUpload::Model model) - : Task(parent), m_model(model), m_skin(skin), m_token(token) +SkinUpload::SkinUpload(QObject *parent, MinecraftAccountPtr acct, QByteArray skin, SkinUpload::Model model) + : Task(parent), m_model(model), m_skin(skin), m_acct(acct) { } void SkinUpload::executeTask() { - QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/skins")); - request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); + QNetworkRequest request(QUrl(m_acct->servicesServerUrl() + "/minecraft/profile/skins")); + QString token = m_acct->accessToken(); + request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toLocal8Bit()); QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpPart skin; diff --git a/launcher/minecraft/services/SkinUpload.h b/launcher/minecraft/services/SkinUpload.h index ac8c5b36..5d0592e0 100644 --- a/launcher/minecraft/services/SkinUpload.h +++ b/launcher/minecraft/services/SkinUpload.h @@ -4,6 +4,7 @@ #include <QtNetwork/QtNetwork> #include <memory> #include "tasks/Task.h" +#include <minecraft/auth/MinecraftAccount.h> typedef shared_qobject_ptr<class SkinUpload> SkinUploadPtr; @@ -18,13 +19,13 @@ public: }; // Note this class takes ownership of the file. - SkinUpload(QObject *parent, QString token, QByteArray skin, Model model = STEVE); + SkinUpload(QObject *parent, MinecraftAccountPtr acct, QByteArray skin, Model model = STEVE); virtual ~SkinUpload() {} private: Model m_model; QByteArray m_skin; - QString m_token; + MinecraftAccountPtr m_acct; shared_qobject_ptr<QNetworkReply> m_reply; protected: virtual void executeTask(); diff --git a/launcher/ui/dialogs/CustomYggdrasilLoginDialog.cpp b/launcher/ui/dialogs/CustomYggdrasilLoginDialog.cpp new file mode 100644 index 00000000..bbbe278d --- /dev/null +++ b/launcher/ui/dialogs/CustomYggdrasilLoginDialog.cpp @@ -0,0 +1,148 @@ +/* Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CustomYggdrasilLoginDialog.h" +#include "ui_CustomYggdrasilLoginDialog.h" + +#include "minecraft/auth/AccountTask.h" + +#include <QtWidgets/QPushButton> + +CustomYggdrasilLoginDialog::CustomYggdrasilLoginDialog(QWidget *parent) : QDialog(parent), ui(new Ui::CustomYggdrasilLoginDialog) +{ + ui->setupUi(this); + ui->progressBar->setVisible(false); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +CustomYggdrasilLoginDialog::~CustomYggdrasilLoginDialog() +{ + delete ui; +} + +QString CustomYggdrasilLoginDialog::fixUrl(QString url) +{ + QString fixed(url); + if (!fixed.contains("://")) { + fixed.prepend("https://"); + } + if (fixed.endsWith("/")) { + fixed = fixed.left(fixed.size() - 1); + } + return fixed; +} + +// Stage 1: User interaction +void CustomYggdrasilLoginDialog::accept() +{ + setUserInputsEnabled(false); + ui->progressBar->setVisible(true); + + // Setup the login task and start it + m_account = MinecraftAccount::createFromUsernameCustomYggdrasil( + ui->userTextBox->text(), + CustomYggdrasilLoginDialog::fixUrl(ui->authServerTextBox->text()), + CustomYggdrasilLoginDialog::fixUrl(ui->accountServerTextBox->text()), + CustomYggdrasilLoginDialog::fixUrl(ui->sessionServerTextBox->text()), + CustomYggdrasilLoginDialog::fixUrl(ui->servicesServerTextBox->text()) + ); + + m_loginTask = m_account->loginCustomYggdrasil(ui->passTextBox->text()); + connect(m_loginTask.get(), &Task::failed, this, &CustomYggdrasilLoginDialog::onTaskFailed); + connect(m_loginTask.get(), &Task::succeeded, this, &CustomYggdrasilLoginDialog::onTaskSucceeded); + connect(m_loginTask.get(), &Task::status, this, &CustomYggdrasilLoginDialog::onTaskStatus); + connect(m_loginTask.get(), &Task::progress, this, &CustomYggdrasilLoginDialog::onTaskProgress); + m_loginTask->start(); +} + +void CustomYggdrasilLoginDialog::setUserInputsEnabled(bool enable) +{ + ui->userTextBox->setEnabled(enable); + ui->passTextBox->setEnabled(enable); + ui->authServerTextBox->setEnabled(enable); + ui->accountServerTextBox->setEnabled(enable); + ui->sessionServerTextBox->setEnabled(enable); + ui->servicesServerTextBox->setEnabled(enable); + ui->buttonBox->setEnabled(enable); +} + +// Enable the OK button only when all textboxes contain something. +void CustomYggdrasilLoginDialog::on_userTextBox_textEdited(const QString &newText) +{ + ui->buttonBox->button(QDialogButtonBox::Ok) + ->setEnabled(!newText.isEmpty() && + !ui->passTextBox->text().isEmpty() && + !ui->authServerTextBox->text().isEmpty() && + !ui->accountServerTextBox->text().isEmpty() && + !ui->sessionServerTextBox->text().isEmpty() && + !ui->servicesServerTextBox->text().isEmpty()); + +} +void CustomYggdrasilLoginDialog::on_passTextBox_textEdited(const QString &newText) +{ + ui->buttonBox->button(QDialogButtonBox::Ok) + ->setEnabled(!newText.isEmpty() && !ui->userTextBox->text().isEmpty()); +} + +void CustomYggdrasilLoginDialog::onTaskFailed(const QString &reason) +{ + // Set message + auto lines = reason.split('\n'); + QString processed; + for(auto line: lines) { + if(line.size()) { + processed += "<font color='red'>" + line + "</font><br />"; + } + else { + processed += "<br />"; + } + } + ui->label->setText(processed); + + // Re-enable user-interaction + setUserInputsEnabled(true); + ui->progressBar->setVisible(false); +} + +void CustomYggdrasilLoginDialog::onTaskSucceeded() +{ + QDialog::accept(); +} + +void CustomYggdrasilLoginDialog::onTaskStatus(const QString &status) +{ + ui->label->setText(status); +} + +void CustomYggdrasilLoginDialog::onTaskProgress(qint64 current, qint64 total) +{ + ui->progressBar->setMaximum(total); + ui->progressBar->setValue(current); +} + +// Public interface +MinecraftAccountPtr CustomYggdrasilLoginDialog::newAccount(QWidget *parent, QString msg) +{ + CustomYggdrasilLoginDialog dlg(parent); + dlg.ui->label->setText(msg); + if (dlg.exec() == QDialog::Accepted) + { + return dlg.m_account; + } + return nullptr; +} diff --git a/launcher/ui/dialogs/CustomYggdrasilLoginDialog.h b/launcher/ui/dialogs/CustomYggdrasilLoginDialog.h new file mode 100644 index 00000000..e2b23bd5 --- /dev/null +++ b/launcher/ui/dialogs/CustomYggdrasilLoginDialog.h @@ -0,0 +1,60 @@ +/* Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <QtWidgets/QDialog> +#include <QtCore/QEventLoop> + +#include "minecraft/auth/MinecraftAccount.h" +#include "tasks/Task.h" + +namespace Ui +{ +class CustomYggdrasilLoginDialog; +} + +class CustomYggdrasilLoginDialog : public QDialog +{ + Q_OBJECT + +public: + ~CustomYggdrasilLoginDialog(); + + static MinecraftAccountPtr newAccount(QWidget *parent, QString message); + +private: + explicit CustomYggdrasilLoginDialog(QWidget *parent = 0); + + void setUserInputsEnabled(bool enable); + +protected +slots: + void accept(); + + void onTaskFailed(const QString &reason); + void onTaskSucceeded(); + void onTaskStatus(const QString &status); + void onTaskProgress(qint64 current, qint64 total); + + void on_userTextBox_textEdited(const QString &newText); + void on_passTextBox_textEdited(const QString &newText); + +private: + QString fixUrl(QString url); + Ui::CustomYggdrasilLoginDialog *ui; + MinecraftAccountPtr m_account; + Task::Ptr m_loginTask; +}; diff --git a/launcher/ui/dialogs/CustomYggdrasilLoginDialog.ui b/launcher/ui/dialogs/CustomYggdrasilLoginDialog.ui new file mode 100644 index 00000000..796aebcf --- /dev/null +++ b/launcher/ui/dialogs/CustomYggdrasilLoginDialog.ui @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>CustomYggdrasilLoginDialog</class> + <widget class="QDialog" name="CustomYggdrasilLoginDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>421</width> + <height>198</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>Add Account</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string notr="true">Message label placeholder.</string> + </property> + <property name="textFormat"> + <enum>Qt::RichText</enum> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="userTextBox"> + <property name="placeholderText"> + <string>Email/username</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="passTextBox"> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + <property name="placeholderText"> + <string>Password</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="authServerTextBox"> + <property name="placeholderText"> + <string>https://authserver.mojang.com</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="accountServerTextBox"> + <property name="placeholderText"> + <string>https://api.mojang.com</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="sessionServerTextBox"> + <property name="placeholderText"> + <string>https://sessionserver.mojang.com</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="servicesServerTextBox"> + <property name="placeholderText"> + <string>https://api.minecraftservices.com</string> + </property> + </widget> + </item> + <item> + <widget class="QProgressBar" name="progressBar"> + <property name="value"> + <number>24</number> + </property> + <property name="textVisible"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/launcher/ui/dialogs/SkinUploadDialog.cpp b/launcher/ui/dialogs/SkinUploadDialog.cpp index 8180ac1f..30f14bb1 100644 --- a/launcher/ui/dialogs/SkinUploadDialog.cpp +++ b/launcher/ui/dialogs/SkinUploadDialog.cpp @@ -120,12 +120,12 @@ void SkinUploadDialog::on_buttonBox_accepted() { model = SkinUpload::ALEX; } - skinUpload.addTask(shared_qobject_ptr<SkinUpload>(new SkinUpload(this, m_acct->accessToken(), FS::read(fileName), model))); + skinUpload.addTask(shared_qobject_ptr<SkinUpload>(new SkinUpload(this, m_acct, FS::read(fileName), model))); } auto selectedCape = ui->capeCombo->currentData().toString(); if(selectedCape != m_acct->accountData()->minecraftProfile.currentCape) { - skinUpload.addTask(shared_qobject_ptr<CapeChange>(new CapeChange(this, m_acct->accessToken(), selectedCape))); + skinUpload.addTask(shared_qobject_ptr<CapeChange>(new CapeChange(this, m_acct, selectedCape))); } if (prog.execWithTask(&skinUpload) != QDialog::Accepted) { diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index 278f45c4..432050ce 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -47,6 +47,7 @@ #include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/OfflineLoginDialog.h" #include "ui/dialogs/LoginDialog.h" +#include "ui/dialogs/CustomYggdrasilLoginDialog.h" #include "ui/dialogs/MSALoginDialog.h" #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/SkinUploadDialog.h" @@ -157,6 +158,22 @@ void AccountListPage::on_actionAddMojang_triggered() } } +void AccountListPage::on_actionAddCustomYggdrasil_triggered() +{ + MinecraftAccountPtr account = CustomYggdrasilLoginDialog::newAccount( + this, + tr("Please enter your email/username, password, and the URLs of your API servers.") + ); + + if (account) + { + m_accounts->addAccount(account); + if (m_accounts->count() == 1) { + m_accounts->setDefaultAccount(account); + } + } +} + void AccountListPage::on_actionAddMicrosoft_triggered() { if(BuildConfig.BUILD_PLATFORM == "osx64") { diff --git a/launcher/ui/pages/global/AccountListPage.h b/launcher/ui/pages/global/AccountListPage.h index 9395e92b..3d163146 100644 --- a/launcher/ui/pages/global/AccountListPage.h +++ b/launcher/ui/pages/global/AccountListPage.h @@ -83,6 +83,7 @@ public: public slots: void on_actionAddMojang_triggered(); + void on_actionAddCustomYggdrasil_triggered(); void on_actionAddMicrosoft_triggered(); void on_actionAddOffline_triggered(); void on_actionRemove_triggered(); diff --git a/launcher/ui/pages/global/AccountListPage.ui b/launcher/ui/pages/global/AccountListPage.ui index 469955b5..a084a538 100644 --- a/launcher/ui/pages/global/AccountListPage.ui +++ b/launcher/ui/pages/global/AccountListPage.ui @@ -54,6 +54,7 @@ </attribute> <addaction name="actionAddMicrosoft"/> <addaction name="actionAddMojang"/> + <addaction name="actionAddCustomYggdrasil"/> <addaction name="actionAddOffline"/> <addaction name="actionRefresh"/> <addaction name="actionRemove"/> @@ -68,6 +69,11 @@ <string>Add &Mojang</string> </property> </action> + <action name="actionAddCustomYggdrasil"> + <property name="text"> + <string>Add &Custom Yggdrasil</string> + </property> + </action> <action name="actionRemove"> <property name="text"> <string>Remo&ve</string> -- 2.41.0 From 11fc89f0c76e7dbc20d8c7085cb328fb88702d27 Mon Sep 17 00:00:00 2001 From: Evan Goode <mail@evangoo.de> Date: Thu, 5 Jan 2023 21:58:08 -0500 Subject: [PATCH 2/6] Allow multiple accounts with the same player UUID Signed-off-by: Evan Goode <mail@evangoo.de> --- launcher/minecraft/auth/AccountList.cpp | 22 +++++++++---------- launcher/minecraft/auth/MinecraftAccount.cpp | 2 +- launcher/minecraft/auth/MinecraftAccount.h | 4 ++++ .../minecraft/auth/flows/CustomYggdrasil.cpp | 12 +++++----- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index edbf2298..6cbb6fb6 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -122,8 +122,17 @@ void AccountList::addAccount(const MinecraftAccountPtr account) // override/replace existing account with the same profileId auto profileId = account->profileId(); - if(profileId.size()) { - auto existingAccount = findAccountByProfileId(profileId); + if(profileId.size() && account->isMojangOrMSA()) { + int existingAccount = -1; + for (int i = 0; i < count(); i++) { + MinecraftAccountPtr existing = at(i); + if (existing->profileId() == profileId && + existing->isMojangOrMSA()) { + existingAccount = i; + break; + } + } + if(existingAccount != -1) { qDebug() << "Replacing old account with a new one with the same profile ID!"; @@ -525,9 +534,6 @@ bool AccountList::loadV2(QJsonObject& root) { if(!profileId.size()) { continue; } - if(findAccountByProfileId(profileId) != -1) { - continue; - } connect(account.get(), &MinecraftAccount::changed, this, &AccountList::accountChanged); connect(account.get(), &MinecraftAccount::activityChanged, this, &AccountList::accountActivityChanged); m_accounts.append(account); @@ -553,12 +559,6 @@ bool AccountList::loadV3(QJsonObject& root) { MinecraftAccountPtr account = MinecraftAccount::loadFromJsonV3(accountObj); if (account.get() != nullptr) { - auto profileId = account->profileId(); - if(profileId.size()) { - if(findAccountByProfileId(profileId) != -1) { - continue; - } - } connect(account.get(), &MinecraftAccount::changed, this, &AccountList::accountChanged); connect(account.get(), &MinecraftAccount::activityChanged, this, &AccountList::accountActivityChanged); m_accounts.append(account); diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index 09a617b1..d715532c 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -91,7 +91,7 @@ MinecraftAccountPtr MinecraftAccount::createFromUsernameCustomYggdrasil( const QString &customServicesServerUrl ) { - MinecraftAccountPtr account = new MinecraftAccount(); + auto account = makeShared<MinecraftAccount>(); account->data.type = AccountType::CustomYggdrasil; account->data.yggdrasilToken.extra["userName"] = username; account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); diff --git a/launcher/minecraft/auth/MinecraftAccount.h b/launcher/minecraft/auth/MinecraftAccount.h index 61a5ab1d..875549b8 100644 --- a/launcher/minecraft/auth/MinecraftAccount.h +++ b/launcher/minecraft/auth/MinecraftAccount.h @@ -173,6 +173,10 @@ public: /* queries */ return data.canMigrateToMSA; } + bool isMojangOrMSA() const { + return data.type == AccountType::Mojang || data.type == AccountType::MSA; + } + bool isMSA() const { return data.type == AccountType::MSA; } diff --git a/launcher/minecraft/auth/flows/CustomYggdrasil.cpp b/launcher/minecraft/auth/flows/CustomYggdrasil.cpp index a8ee6f3d..bda0eb2e 100644 --- a/launcher/minecraft/auth/flows/CustomYggdrasil.cpp +++ b/launcher/minecraft/auth/flows/CustomYggdrasil.cpp @@ -9,9 +9,9 @@ CustomYggdrasilRefresh::CustomYggdrasilRefresh( AccountData *data, QObject *parent ) : AuthFlow(data, parent) { - m_steps.append(new YggdrasilStep(m_data, QString())); - m_steps.append(new MinecraftProfileStepMojang(m_data)); - m_steps.append(new GetSkinStep(m_data)); + m_steps.append(makeShared<YggdrasilStep>(m_data, QString())); + m_steps.append(makeShared<MinecraftProfileStepMojang>(m_data)); + m_steps.append(makeShared<GetSkinStep>(m_data)); } CustomYggdrasilLogin::CustomYggdrasilLogin( @@ -19,7 +19,7 @@ CustomYggdrasilLogin::CustomYggdrasilLogin( QString password, QObject *parent ): AuthFlow(data, parent), m_password(password) { - m_steps.append(new YggdrasilStep(m_data, m_password)); - m_steps.append(new MinecraftProfileStepMojang(m_data)); - m_steps.append(new GetSkinStep(m_data)); + m_steps.append(makeShared<YggdrasilStep>(m_data, m_password)); + m_steps.append(makeShared<MinecraftProfileStepMojang>(m_data)); + m_steps.append(makeShared<GetSkinStep>(m_data)); } -- 2.41.0 From d14b67f328dd50a8403c7d2454463f3159323556 Mon Sep 17 00:00:00 2001 From: Evan Goode <mail@evangoo.de> Date: Tue, 16 May 2023 00:52:37 -0400 Subject: [PATCH 3/6] Use correct services server URL for SkinDelete Signed-off-by: Evan Goode <mail@evangoo.de> --- launcher/minecraft/services/SkinDelete.cpp | 9 +++++---- launcher/minecraft/services/SkinDelete.h | 5 +++-- launcher/ui/pages/global/AccountListPage.cpp | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/launcher/minecraft/services/SkinDelete.cpp b/launcher/minecraft/services/SkinDelete.cpp index fbaaeacb..120c7a24 100644 --- a/launcher/minecraft/services/SkinDelete.cpp +++ b/launcher/minecraft/services/SkinDelete.cpp @@ -40,15 +40,16 @@ #include "Application.h" -SkinDelete::SkinDelete(QObject *parent, QString token) - : Task(parent), m_token(token) +SkinDelete::SkinDelete(QObject *parent, MinecraftAccountPtr acct) + : Task(parent), m_acct(acct) { } void SkinDelete::executeTask() { - QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/skins/active")); - request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); + QNetworkRequest request(QUrl(m_acct->servicesServerUrl() + "/minecraft/profile/skins/active")); + QString token = m_acct->accessToken(); + request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toLocal8Bit()); QNetworkReply *rep = APPLICATION->network()->deleteResource(request); m_reply = shared_qobject_ptr<QNetworkReply>(rep); diff --git a/launcher/minecraft/services/SkinDelete.h b/launcher/minecraft/services/SkinDelete.h index b9a1c9d3..3948f712 100644 --- a/launcher/minecraft/services/SkinDelete.h +++ b/launcher/minecraft/services/SkinDelete.h @@ -3,6 +3,7 @@ #include <QFile> #include <QtNetwork/QtNetwork> #include "tasks/Task.h" +#include <minecraft/auth/MinecraftAccount.h> typedef shared_qobject_ptr<class SkinDelete> SkinDeletePtr; @@ -10,11 +11,11 @@ class SkinDelete : public Task { Q_OBJECT public: - SkinDelete(QObject *parent, QString token); + SkinDelete(QObject *parent, MinecraftAccountPtr acct); virtual ~SkinDelete() = default; private: - QString m_token; + MinecraftAccountPtr m_acct; shared_qobject_ptr<QNetworkReply> m_reply; protected: diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index 432050ce..49de9bb3 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -332,7 +332,7 @@ void AccountListPage::on_actionDeleteSkin_triggered() QModelIndex selected = selection.first(); MinecraftAccountPtr account = selected.data(AccountList::PointerRole).value<MinecraftAccountPtr>(); ProgressDialog prog(this); - auto deleteSkinTask = std::make_shared<SkinDelete>(this, account->accessToken()); + auto deleteSkinTask = std::make_shared<SkinDelete>(this, account); if (prog.execWithTask((Task*)deleteSkinTask.get()) != QDialog::Accepted) { CustomMessageBox::selectable(this, tr("Skin Delete"), tr("Failed to delete current skin!"), QMessageBox::Warning)->exec(); return; -- 2.41.0 From 8575571783b0e346c759b000cf8d7de98d6cd550 Mon Sep 17 00:00:00 2001 From: Evan Goode <mail@evangoo.de> Date: Tue, 16 May 2023 01:18:19 -0400 Subject: [PATCH 4/6] Correctly use CustomYggdrasilRefresh, add warning message Signed-off-by: Evan Goode <mail@evangoo.de> --- launcher/minecraft/auth/MinecraftAccount.cpp | 3 +++ launcher/minecraft/auth/flows/CustomYggdrasil.cpp | 1 - launcher/ui/pages/global/AccountListPage.cpp | 6 +++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index d715532c..55ce2fd8 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -205,6 +205,9 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::refresh() { else if(data.type == AccountType::Offline) { m_currentTask.reset(new OfflineRefresh(&data)); } + else if (data.type == AccountType::CustomYggdrasil) { + m_currentTask.reset(new CustomYggdrasilRefresh(&data)); + } else { m_currentTask.reset(new MojangRefresh(&data)); } diff --git a/launcher/minecraft/auth/flows/CustomYggdrasil.cpp b/launcher/minecraft/auth/flows/CustomYggdrasil.cpp index bda0eb2e..5fbb5654 100644 --- a/launcher/minecraft/auth/flows/CustomYggdrasil.cpp +++ b/launcher/minecraft/auth/flows/CustomYggdrasil.cpp @@ -2,7 +2,6 @@ #include "minecraft/auth/steps/YggdrasilStep.h" #include "minecraft/auth/steps/MinecraftProfileStepMojang.h" -#include "minecraft/auth/steps/MigrationEligibilityStep.h" #include "minecraft/auth/steps/GetSkinStep.h" CustomYggdrasilRefresh::CustomYggdrasilRefresh( diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index 49de9bb3..bb04f127 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -175,7 +175,11 @@ void AccountListPage::on_actionAddCustomYggdrasil_triggered() MinecraftAccountPtr account = CustomYggdrasilLoginDialog::newAccount( this, - tr("Please enter your email/username, password, and the URLs of your API servers.") + tr( + "Please enter your username (sometimes an email address), password, and the URLs of your API servers." + "<br><br>" + "<b>Caution!</b> Your username and password will be sent to the authentication server you specify!" + ) ); if (account) -- 2.41.0 From fdbb8b63717bc2a7e6bb608b0d1301c1e94005a0 Mon Sep 17 00:00:00 2001 From: Evan Goode <mail@evangoo.de> Date: Thu, 15 Jun 2023 16:20:29 -0400 Subject: [PATCH 5/6] Custom Yggdrasil: Readability fixes Also made the MinecraftEntitlement for Custom Yggdrasil accounts work just like offline accounts---instead of checking the reply from the auth server, Custom Yggdrasil accounts are granted canPlayMinecraft and ownsMinecraft when they are created, in MinecraftAccount.cpp. Signed-off-by: Evan Goode <mail@evangoo.de> --- launcher/minecraft/auth/AccountData.cpp | 4 +-- launcher/minecraft/auth/AccountList.cpp | 25 ++++++++----------- launcher/minecraft/auth/MinecraftAccount.cpp | 17 +++++++------ launcher/minecraft/auth/Yggdrasil.cpp | 2 -- .../auth/steps/MinecraftProfileStep.cpp | 4 +-- .../auth/steps/MinecraftProfileStepMojang.cpp | 4 +-- launcher/minecraft/services/CapeChange.cpp | 10 ++++---- launcher/minecraft/services/CapeChange.h | 4 +-- launcher/minecraft/services/SkinDelete.cpp | 6 ++--- launcher/minecraft/services/SkinDelete.h | 2 +- launcher/minecraft/services/SkinUpload.cpp | 6 ++--- launcher/minecraft/services/SkinUpload.h | 2 +- launcher/ui/dialogs/SkinUploadDialog.cpp | 8 +++--- launcher/ui/dialogs/SkinUploadDialog.h | 2 +- 14 files changed, 44 insertions(+), 52 deletions(-) diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp index 1f32efd4..3235c79c 100644 --- a/launcher/minecraft/auth/AccountData.cpp +++ b/launcher/minecraft/auth/AccountData.cpp @@ -524,9 +524,7 @@ QString AccountData::profileName() const { QString AccountData::accountDisplayString() const { switch(type) { - case AccountType::Mojang: { - return userName(); - } + case AccountType::Mojang: case AccountType::CustomYggdrasil: { return userName(); } diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index 6cbb6fb6..ecd27ba4 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -123,27 +123,22 @@ void AccountList::addAccount(const MinecraftAccountPtr account) // override/replace existing account with the same profileId auto profileId = account->profileId(); if(profileId.size() && account->isMojangOrMSA()) { - int existingAccount = -1; - for (int i = 0; i < count(); i++) { - MinecraftAccountPtr existing = at(i); - if (existing->profileId() == profileId && - existing->isMojangOrMSA()) { - existingAccount = i; - break; - } - } + auto iter = std::find_if(m_accounts.constBegin(), m_accounts.constEnd(), [&](const MinecraftAccountPtr & existing) { + return existing->profileId() == profileId && existing->isMojangOrMSA(); + }); - if(existingAccount != -1) { + if(iter != m_accounts.constEnd()) { qDebug() << "Replacing old account with a new one with the same profile ID!"; - MinecraftAccountPtr existingAccountPtr = m_accounts[existingAccount]; - m_accounts[existingAccount] = account; - if(m_defaultAccount == existingAccountPtr) { + MinecraftAccountPtr existingAccount = *iter; + const auto existingAccountIndex = std::distance(m_accounts.constBegin(), iter); + m_accounts[existingAccountIndex] = account; + if(m_defaultAccount == existingAccount) { m_defaultAccount = account; } // disconnect notifications for changes in the account being replaced - existingAccountPtr->disconnect(this); - emit dataChanged(index(existingAccount), index(existingAccount, columnCount(QModelIndex()) - 1)); + existingAccount->disconnect(this); + emit dataChanged(index(existingAccountIndex), index(existingAccountIndex, columnCount(QModelIndex()) - 1)); onListChanged(); return; } diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index 55ce2fd8..26c86517 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -40,7 +40,6 @@ #include <QUuid> #include <QJsonObject> #include <QJsonArray> -#include <QRegularExpression> #include <QStringList> #include <QJsonDocument> @@ -54,7 +53,7 @@ #include "flows/Offline.h" MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) { - data.internalId = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); + data.internalId = QUuid::createUuid().toString(QUuid::Id128); } @@ -79,7 +78,7 @@ MinecraftAccountPtr MinecraftAccount::createFromUsername(const QString &username auto account = makeShared<MinecraftAccount>(); account->data.type = AccountType::Mojang; account->data.yggdrasilToken.extra["userName"] = username; - account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); + account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString(QUuid::Id128); return account; } @@ -94,7 +93,9 @@ MinecraftAccountPtr MinecraftAccount::createFromUsernameCustomYggdrasil( auto account = makeShared<MinecraftAccount>(); account->data.type = AccountType::CustomYggdrasil; account->data.yggdrasilToken.extra["userName"] = username; - account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); + account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString(QUuid::Id128); + account->data.minecraftEntitlement.ownsMinecraft = true; + account->data.minecraftEntitlement.canPlayMinecraft = true; account->data.customAuthServerUrl = customAuthServerUrl; account->data.customAccountServerUrl = customAccountServerUrl; @@ -118,10 +119,10 @@ MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username) account->data.yggdrasilToken.validity = Katabasis::Validity::Certain; account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc(); account->data.yggdrasilToken.extra["userName"] = username; - account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); + account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString(QUuid::Id128); account->data.minecraftEntitlement.ownsMinecraft = true; account->data.minecraftEntitlement.canPlayMinecraft = true; - account->data.minecraftProfile.id = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); + account->data.minecraftProfile.id = QUuid::createUuid().toString(QUuid::Id128); account->data.minecraftProfile.name = username; account->data.minecraftProfile.validity = Katabasis::Validity::Certain; return account; @@ -165,8 +166,8 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::loginCustomYggdrasil(QString p Q_ASSERT(m_currentTask.get() == nullptr); m_currentTask.reset(new CustomYggdrasilLogin(&data, password)); - connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); - connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); + connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); + connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; diff --git a/launcher/minecraft/auth/Yggdrasil.cpp b/launcher/minecraft/auth/Yggdrasil.cpp index f5ddf776..43fda881 100644 --- a/launcher/minecraft/auth/Yggdrasil.cpp +++ b/launcher/minecraft/auth/Yggdrasil.cpp @@ -73,8 +73,6 @@ void Yggdrasil::refresh() { QJsonObject req; req.insert("clientToken", m_data->clientToken()); req.insert("accessToken", m_data->accessToken()); - - qDebug() << "refreshing, access token is" << m_data->accessToken(); /* { auto currentProfile = m_account->currentProfile(); diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp b/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp index 16230e04..6cfa7c1c 100644 --- a/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp +++ b/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp @@ -44,7 +44,7 @@ void MinecraftProfileStep::onRequestDone( qCDebug(authCredentials()) << data; if (error == QNetworkReply::ContentNotFoundError) { // NOTE: Succeed even if we do not have a profile. This is a valid account state. - if(m_data->type == AccountType::Mojang || m_data->type == AccountType::CustomYggdrasil) { + if(m_data->type == AccountType::Mojang) { m_data->minecraftEntitlement.canPlayMinecraft = false; m_data->minecraftEntitlement.ownsMinecraft = false; } @@ -87,7 +87,7 @@ void MinecraftProfileStep::onRequestDone( return; } - if(m_data->type == AccountType::Mojang || m_data->type == AccountType::CustomYggdrasil) { + if(m_data->type == AccountType::Mojang) { auto validProfile = m_data->minecraftProfile.validity == Katabasis::Validity::Certain; m_data->minecraftEntitlement.canPlayMinecraft = validProfile; m_data->minecraftEntitlement.ownsMinecraft = validProfile; diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp index f8d4baed..08174ece 100644 --- a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp +++ b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp @@ -48,7 +48,7 @@ void MinecraftProfileStepMojang::onRequestDone( qCDebug(authCredentials()) << data; if (error == QNetworkReply::ContentNotFoundError) { // NOTE: Succeed even if we do not have a profile. This is a valid account state. - if(m_data->type == AccountType::Mojang || m_data->type == AccountType::CustomYggdrasil) { + if(m_data->type == AccountType::Mojang) { m_data->minecraftEntitlement.canPlayMinecraft = false; m_data->minecraftEntitlement.ownsMinecraft = false; } @@ -91,7 +91,7 @@ void MinecraftProfileStepMojang::onRequestDone( return; } - if(m_data->type == AccountType::Mojang || m_data->type == AccountType::CustomYggdrasil) { + if(m_data->type == AccountType::Mojang) { auto validProfile = m_data->minecraftProfile.validity == Katabasis::Validity::Certain; m_data->minecraftEntitlement.canPlayMinecraft = validProfile; m_data->minecraftEntitlement.ownsMinecraft = validProfile; diff --git a/launcher/minecraft/services/CapeChange.cpp b/launcher/minecraft/services/CapeChange.cpp index e6e70272..09a57863 100644 --- a/launcher/minecraft/services/CapeChange.cpp +++ b/launcher/minecraft/services/CapeChange.cpp @@ -41,13 +41,13 @@ #include "Application.h" CapeChange::CapeChange(QObject *parent, MinecraftAccountPtr acct, QString cape) - : Task(parent), m_capeId(cape), m_acct(acct) + : Task(parent), m_capeId(cape), m_account(acct) { } void CapeChange::setCape(QString& cape) { - QNetworkRequest request(QUrl(m_acct->servicesServerUrl() + "/minecraft/profile/capes/active")); - QString token = m_acct->accessToken(); + QNetworkRequest request(QUrl(m_account->servicesServerUrl() + "/minecraft/profile/capes/active")); + QString token = m_account->accessToken(); auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId); request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toLocal8Bit()); QNetworkReply *rep = APPLICATION->network()->put(request, requestString.toUtf8()); @@ -66,8 +66,8 @@ void CapeChange::setCape(QString& cape) { } void CapeChange::clearCape() { - QNetworkRequest request(QUrl(m_acct->servicesServerUrl() + "/minecraft/profile/capes/active")); - QString token = m_acct->accessToken(); + QNetworkRequest request(QUrl(m_account->servicesServerUrl() + "/minecraft/profile/capes/active")); + QString token = m_account->accessToken(); auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId); request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toLocal8Bit()); QNetworkReply *rep = APPLICATION->network()->deleteResource(request); diff --git a/launcher/minecraft/services/CapeChange.h b/launcher/minecraft/services/CapeChange.h index 494b59e0..8f94d89b 100644 --- a/launcher/minecraft/services/CapeChange.h +++ b/launcher/minecraft/services/CapeChange.h @@ -11,7 +11,7 @@ class CapeChange : public Task { Q_OBJECT public: - CapeChange(QObject *parent, MinecraftAccountPtr m_acct, QString capeId); + CapeChange(QObject *parent, MinecraftAccountPtr m_account, QString capeId); virtual ~CapeChange() {} private: @@ -20,7 +20,7 @@ private: private: QString m_capeId; - MinecraftAccountPtr m_acct; + MinecraftAccountPtr m_account; shared_qobject_ptr<QNetworkReply> m_reply; protected: diff --git a/launcher/minecraft/services/SkinDelete.cpp b/launcher/minecraft/services/SkinDelete.cpp index 120c7a24..a350d15f 100644 --- a/launcher/minecraft/services/SkinDelete.cpp +++ b/launcher/minecraft/services/SkinDelete.cpp @@ -41,14 +41,14 @@ #include "Application.h" SkinDelete::SkinDelete(QObject *parent, MinecraftAccountPtr acct) - : Task(parent), m_acct(acct) + : Task(parent), m_account(acct) { } void SkinDelete::executeTask() { - QNetworkRequest request(QUrl(m_acct->servicesServerUrl() + "/minecraft/profile/skins/active")); - QString token = m_acct->accessToken(); + QNetworkRequest request(QUrl(m_account->servicesServerUrl() + "/minecraft/profile/skins/active")); + QString token = m_account->accessToken(); request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toLocal8Bit()); QNetworkReply *rep = APPLICATION->network()->deleteResource(request); m_reply = shared_qobject_ptr<QNetworkReply>(rep); diff --git a/launcher/minecraft/services/SkinDelete.h b/launcher/minecraft/services/SkinDelete.h index 3948f712..ee5a4b97 100644 --- a/launcher/minecraft/services/SkinDelete.h +++ b/launcher/minecraft/services/SkinDelete.h @@ -15,7 +15,7 @@ public: virtual ~SkinDelete() = default; private: - MinecraftAccountPtr m_acct; + MinecraftAccountPtr m_account; shared_qobject_ptr<QNetworkReply> m_reply; protected: diff --git a/launcher/minecraft/services/SkinUpload.cpp b/launcher/minecraft/services/SkinUpload.cpp index c7eaa51e..b19f231c 100644 --- a/launcher/minecraft/services/SkinUpload.cpp +++ b/launcher/minecraft/services/SkinUpload.cpp @@ -52,14 +52,14 @@ QByteArray getVariant(SkinUpload::Model model) { } SkinUpload::SkinUpload(QObject *parent, MinecraftAccountPtr acct, QByteArray skin, SkinUpload::Model model) - : Task(parent), m_model(model), m_skin(skin), m_acct(acct) + : Task(parent), m_model(model), m_skin(skin), m_account(acct) { } void SkinUpload::executeTask() { - QNetworkRequest request(QUrl(m_acct->servicesServerUrl() + "/minecraft/profile/skins")); - QString token = m_acct->accessToken(); + QNetworkRequest request(QUrl(m_account->servicesServerUrl() + "/minecraft/profile/skins")); + QString token = m_account->accessToken(); request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toLocal8Bit()); QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); diff --git a/launcher/minecraft/services/SkinUpload.h b/launcher/minecraft/services/SkinUpload.h index 5d0592e0..61cae484 100644 --- a/launcher/minecraft/services/SkinUpload.h +++ b/launcher/minecraft/services/SkinUpload.h @@ -25,7 +25,7 @@ public: private: Model m_model; QByteArray m_skin; - MinecraftAccountPtr m_acct; + MinecraftAccountPtr m_account; shared_qobject_ptr<QNetworkReply> m_reply; protected: virtual void executeTask(); diff --git a/launcher/ui/dialogs/SkinUploadDialog.cpp b/launcher/ui/dialogs/SkinUploadDialog.cpp index 30f14bb1..43459833 100644 --- a/launcher/ui/dialogs/SkinUploadDialog.cpp +++ b/launcher/ui/dialogs/SkinUploadDialog.cpp @@ -120,12 +120,12 @@ void SkinUploadDialog::on_buttonBox_accepted() { model = SkinUpload::ALEX; } - skinUpload.addTask(shared_qobject_ptr<SkinUpload>(new SkinUpload(this, m_acct, FS::read(fileName), model))); + skinUpload.addTask(shared_qobject_ptr<SkinUpload>(new SkinUpload(this, m_account, FS::read(fileName), model))); } auto selectedCape = ui->capeCombo->currentData().toString(); - if(selectedCape != m_acct->accountData()->minecraftProfile.currentCape) { - skinUpload.addTask(shared_qobject_ptr<CapeChange>(new CapeChange(this, m_acct, selectedCape))); + if(selectedCape != m_account->accountData()->minecraftProfile.currentCape) { + skinUpload.addTask(shared_qobject_ptr<CapeChange>(new CapeChange(this, m_account, selectedCape))); } if (prog.execWithTask(&skinUpload) != QDialog::Accepted) { @@ -150,7 +150,7 @@ void SkinUploadDialog::on_skinBrowseBtn_clicked() } SkinUploadDialog::SkinUploadDialog(MinecraftAccountPtr acct, QWidget *parent) - :QDialog(parent), m_acct(acct), ui(new Ui::SkinUploadDialog) + :QDialog(parent), m_account(acct), ui(new Ui::SkinUploadDialog) { ui->setupUi(this); diff --git a/launcher/ui/dialogs/SkinUploadDialog.h b/launcher/ui/dialogs/SkinUploadDialog.h index 84d17dc6..4f4a7188 100644 --- a/launcher/ui/dialogs/SkinUploadDialog.h +++ b/launcher/ui/dialogs/SkinUploadDialog.h @@ -22,7 +22,7 @@ public slots: void on_skinBrowseBtn_clicked(); protected: - MinecraftAccountPtr m_acct; + MinecraftAccountPtr m_account; private: Ui::SkinUploadDialog *ui; -- 2.41.0 From 242c1e43f9c6b423a77d41be30c19df2880dcaaf Mon Sep 17 00:00:00 2001 From: Evan Goode <mail@evangoo.de> Date: Sat, 24 Jun 2023 14:15:14 -0400 Subject: [PATCH 6/6] Custom Yggdrasil: Add extra confirmation dialog Signed-off-by: Evan Goode <mail@evangoo.de> --- .../ui/dialogs/CustomYggdrasilLoginDialog.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/launcher/ui/dialogs/CustomYggdrasilLoginDialog.cpp b/launcher/ui/dialogs/CustomYggdrasilLoginDialog.cpp index bbbe278d..9f705ac1 100644 --- a/launcher/ui/dialogs/CustomYggdrasilLoginDialog.cpp +++ b/launcher/ui/dialogs/CustomYggdrasilLoginDialog.cpp @@ -15,6 +15,7 @@ #include "CustomYggdrasilLoginDialog.h" #include "ui_CustomYggdrasilLoginDialog.h" +#include "ui/dialogs/CustomMessageBox.h" #include "minecraft/auth/AccountTask.h" @@ -50,13 +51,27 @@ QString CustomYggdrasilLoginDialog::fixUrl(QString url) // Stage 1: User interaction void CustomYggdrasilLoginDialog::accept() { + auto fixedAuthUrl = CustomYggdrasilLoginDialog::fixUrl(ui->authServerTextBox->text()); + + auto response = CustomMessageBox::selectable(this, QObject::tr("Confirm account creation"), + QObject::tr( + "Warning: you are about to send the username and password you entered to an " + "unofficial, third-party authentication server:\n" + "%1\n\n" + "Never use your Mojang or Microsoft password for a third-party account!\n\n" + "Are you sure you want to proceed?" + ).arg(fixedAuthUrl), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)->exec(); + if (response != QMessageBox::Yes) + return; + setUserInputsEnabled(false); ui->progressBar->setVisible(true); // Setup the login task and start it m_account = MinecraftAccount::createFromUsernameCustomYggdrasil( ui->userTextBox->text(), - CustomYggdrasilLoginDialog::fixUrl(ui->authServerTextBox->text()), + fixedAuthUrl, CustomYggdrasilLoginDialog::fixUrl(ui->accountServerTextBox->text()), CustomYggdrasilLoginDialog::fixUrl(ui->sessionServerTextBox->text()), CustomYggdrasilLoginDialog::fixUrl(ui->servicesServerTextBox->text()) -- 2.41.0