Merge pull request #1598 from Kaydax/authlib
Add support for authlibinjector
This commit is contained in:
commit
6431265a73
@ -818,6 +818,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
m_metacache->addBase("translations", QDir("translations").absolutePath());
|
||||
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
|
||||
m_metacache->addBase("meta", QDir("meta").absolutePath());
|
||||
m_metacache->addBase("authlibinjector", QDir("cache/authlibinjector").absolutePath());
|
||||
m_metacache->Load();
|
||||
qDebug() << "<> Cache initialized.";
|
||||
}
|
||||
|
@ -205,11 +205,15 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/auth/flows/AuthFlow.h
|
||||
minecraft/auth/flows/Mojang.cpp
|
||||
minecraft/auth/flows/Mojang.h
|
||||
minecraft/auth/flows/AuthlibInjector.cpp
|
||||
minecraft/auth/flows/AuthlibInjector.h
|
||||
minecraft/auth/flows/MSA.cpp
|
||||
minecraft/auth/flows/MSA.h
|
||||
minecraft/auth/flows/Offline.cpp
|
||||
minecraft/auth/flows/Offline.h
|
||||
|
||||
minecraft/auth/steps/AuthlibInjectorStep.cpp
|
||||
minecraft/auth/steps/AuthlibInjectorStep.h
|
||||
minecraft/auth/steps/OfflineStep.cpp
|
||||
minecraft/auth/steps/OfflineStep.h
|
||||
minecraft/auth/steps/EntitlementsStep.cpp
|
||||
@ -249,6 +253,8 @@ set(MINECRAFT_SOURCES
|
||||
|
||||
minecraft/launch/ClaimAccount.cpp
|
||||
minecraft/launch/ClaimAccount.h
|
||||
minecraft/launch/ConfigureAuthlibInjector.cpp
|
||||
minecraft/launch/ConfigureAuthlibInjector.h
|
||||
minecraft/launch/CreateGameFolders.cpp
|
||||
minecraft/launch/CreateGameFolders.h
|
||||
minecraft/launch/ModMinecraftJar.cpp
|
||||
|
@ -60,6 +60,7 @@
|
||||
#include "launch/steps/QuitAfterGameStop.h"
|
||||
|
||||
#include "minecraft/launch/LauncherPartLaunch.h"
|
||||
#include "minecraft/launch/ConfigureAuthlibInjector.h"
|
||||
#include "minecraft/launch/DirectJavaLaunch.h"
|
||||
#include "minecraft/launch/ModMinecraftJar.h"
|
||||
#include "minecraft/launch/ClaimAccount.h"
|
||||
@ -388,6 +389,11 @@ QStringList MinecraftInstance::javaArguments()
|
||||
{
|
||||
QStringList args;
|
||||
|
||||
if (!m_authlibinjector_javaagent->isNull())
|
||||
{
|
||||
args.append(QString("-javaagent:%1").arg(*m_authlibinjector_javaagent));
|
||||
}
|
||||
|
||||
// custom args go first. we want to override them if we have our own here.
|
||||
args.append(extraArguments());
|
||||
|
||||
@ -990,6 +996,12 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
||||
process->appendStep(step);
|
||||
}
|
||||
|
||||
*m_authlibinjector_javaagent = QString();
|
||||
if (!session->authlib_injector_base_url.isNull())
|
||||
{
|
||||
process->appendStep(new ConfigureAuthlibInjector(pptr, session->authlib_injector_base_url, m_authlibinjector_javaagent));
|
||||
}
|
||||
|
||||
// if we aren't in offline mode,.
|
||||
if(session->status != AuthSession::PlayableOffline)
|
||||
{
|
||||
|
@ -173,6 +173,7 @@ protected: // data
|
||||
mutable std::shared_ptr<TexturePackFolderModel> m_texture_pack_list;
|
||||
mutable std::shared_ptr<WorldList> m_world_list;
|
||||
mutable std::shared_ptr<GameOptions> m_game_options;
|
||||
mutable std::shared_ptr<QString> m_authlibinjector_javaagent = std::make_shared<QString>();
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<MinecraftInstance> MinecraftInstancePtr;
|
||||
|
@ -350,6 +350,8 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
|
||||
type = AccountType::MSA;
|
||||
} else if (typeS == "Mojang") {
|
||||
type = AccountType::Mojang;
|
||||
} else if (typeS == "Authlib-Injector") {
|
||||
type = AccountType::AuthlibInjector;
|
||||
} else if (typeS == "Offline") {
|
||||
type = AccountType::Offline;
|
||||
} else {
|
||||
@ -362,6 +364,10 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
|
||||
canMigrateToMSA = data.value("canMigrateToMSA").toBool(false);
|
||||
}
|
||||
|
||||
if(type == AccountType::AuthlibInjector) {
|
||||
authlibInjectorBaseUrl = data.value("authlibInjectorUrl").toString();
|
||||
}
|
||||
|
||||
if(type == AccountType::MSA) {
|
||||
auto clientIDV = data.value("msa-client-id");
|
||||
if (clientIDV.isString()) {
|
||||
@ -405,8 +411,10 @@ QJsonObject AccountData::saveState() const {
|
||||
tokenToJSONV3(output, userToken, "utoken");
|
||||
tokenToJSONV3(output, xboxApiToken, "xrp-main");
|
||||
tokenToJSONV3(output, mojangservicesToken, "xrp-mc");
|
||||
}
|
||||
else if (type == AccountType::Offline) {
|
||||
} else if (type == AccountType::AuthlibInjector) {
|
||||
output["type"] = "Authlib-Injector";
|
||||
output["authlibInjectorUrl"] = authlibInjectorBaseUrl;
|
||||
} else if (type == AccountType::Offline) {
|
||||
output["type"] = "Offline";
|
||||
}
|
||||
|
||||
@ -428,14 +436,14 @@ QString AccountData::accessToken() const {
|
||||
}
|
||||
|
||||
QString AccountData::clientToken() const {
|
||||
if(type != AccountType::Mojang) {
|
||||
if(type != AccountType::Mojang && type != AccountType::AuthlibInjector) {
|
||||
return QString();
|
||||
}
|
||||
return yggdrasilToken.extra["clientToken"].toString();
|
||||
}
|
||||
|
||||
void AccountData::setClientToken(QString clientToken) {
|
||||
if(type != AccountType::Mojang) {
|
||||
if(type != AccountType::Mojang && type != AccountType::AuthlibInjector) {
|
||||
return;
|
||||
}
|
||||
yggdrasilToken.extra["clientToken"] = clientToken;
|
||||
@ -449,7 +457,7 @@ void AccountData::generateClientTokenIfMissing() {
|
||||
}
|
||||
|
||||
void AccountData::invalidateClientToken() {
|
||||
if(type != AccountType::Mojang) {
|
||||
if(type != AccountType::Mojang && type != AccountType::AuthlibInjector) {
|
||||
return;
|
||||
}
|
||||
yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{-}]"));
|
||||
@ -470,6 +478,7 @@ QString AccountData::profileName() const {
|
||||
|
||||
QString AccountData::accountDisplayString() const {
|
||||
switch(type) {
|
||||
case AccountType::AuthlibInjector:
|
||||
case AccountType::Mojang: {
|
||||
return userName();
|
||||
}
|
||||
|
@ -74,6 +74,7 @@ struct MinecraftProfile {
|
||||
enum class AccountType {
|
||||
MSA,
|
||||
Mojang,
|
||||
AuthlibInjector,
|
||||
Offline
|
||||
};
|
||||
|
||||
@ -115,6 +116,9 @@ struct AccountData {
|
||||
QString lastError() const;
|
||||
|
||||
AccountType type = AccountType::MSA;
|
||||
QString authlibInjectorBaseUrl;
|
||||
QString authlibInjectorApiLocation;
|
||||
|
||||
bool legacy = false;
|
||||
bool canMigrateToMSA = false;
|
||||
|
||||
|
@ -38,8 +38,12 @@ struct AuthSession
|
||||
QString player_name;
|
||||
// profile ID
|
||||
QString uuid;
|
||||
// 'legacy' or 'mojang', depending on account type
|
||||
// 'legacy' or 'mojang' or 'authlib-injector', depending on account type
|
||||
QString user_type;
|
||||
|
||||
// If not using authlib injector, this is blank.
|
||||
QString authlib_injector_base_url;
|
||||
|
||||
// Did the auth server reply?
|
||||
bool auth_server_online = false;
|
||||
// Did the user request online mode?
|
||||
|
@ -50,7 +50,9 @@
|
||||
|
||||
#include "flows/MSA.h"
|
||||
#include "flows/Mojang.h"
|
||||
#include "flows/AuthlibInjector.h"
|
||||
#include "flows/Offline.h"
|
||||
#include "minecraft/auth/AccountData.h"
|
||||
|
||||
MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) {
|
||||
data.internalId = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
|
||||
@ -82,6 +84,16 @@ MinecraftAccountPtr MinecraftAccount::createFromUsername(const QString &username
|
||||
return account;
|
||||
}
|
||||
|
||||
MinecraftAccountPtr MinecraftAccount::createAuthlibInjectorFromUsername(const QString &username, QString baseUrl)
|
||||
{
|
||||
MinecraftAccountPtr account = createFromUsername(username);
|
||||
account->data.type = AccountType::AuthlibInjector;
|
||||
account->data.authlibInjectorBaseUrl = baseUrl;
|
||||
account->data.minecraftEntitlement.ownsMinecraft = true;
|
||||
account->data.minecraftEntitlement.canPlayMinecraft = true;
|
||||
return account;
|
||||
}
|
||||
|
||||
MinecraftAccountPtr MinecraftAccount::createBlankMSA()
|
||||
{
|
||||
MinecraftAccountPtr account(new MinecraftAccount());
|
||||
@ -132,7 +144,14 @@ QPixmap MinecraftAccount::getFace() const {
|
||||
shared_qobject_ptr<AccountTask> MinecraftAccount::login(QString password) {
|
||||
Q_ASSERT(m_currentTask.get() == nullptr);
|
||||
|
||||
m_currentTask.reset(new MojangLogin(&data, password));
|
||||
if (data.type == AccountType::Mojang)
|
||||
{
|
||||
m_currentTask.reset(new MojangLogin(&data, password));
|
||||
}
|
||||
else if (data.type == AccountType::AuthlibInjector)
|
||||
{
|
||||
m_currentTask.reset(new AuthlibInjectorLogin(&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")); });
|
||||
@ -173,6 +192,9 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::refresh() {
|
||||
else if(data.type == AccountType::Offline) {
|
||||
m_currentTask.reset(new OfflineRefresh(&data));
|
||||
}
|
||||
else if(data.type == AccountType::AuthlibInjector) {
|
||||
m_currentTask.reset(new AuthlibInjectorRefresh(&data));
|
||||
}
|
||||
else {
|
||||
m_currentTask.reset(new MojangRefresh(&data));
|
||||
}
|
||||
@ -300,8 +322,9 @@ void MinecraftAccount::fillSession(AuthSessionPtr session)
|
||||
session->player_name = data.profileName();
|
||||
// profile ID
|
||||
session->uuid = data.profileId();
|
||||
// 'legacy' or 'mojang', depending on account type
|
||||
// 'legacy' or 'mojang', or 'authlib-injector' depending on account type
|
||||
session->user_type = typeString();
|
||||
session->authlib_injector_base_url = data.authlibInjectorBaseUrl;
|
||||
if (!session->access_token.isEmpty())
|
||||
{
|
||||
session->session = "token:" + data.accessToken() + ":" + data.profileId();
|
||||
|
@ -91,6 +91,8 @@ public: /* construction */
|
||||
|
||||
static MinecraftAccountPtr createFromUsername(const QString &username);
|
||||
|
||||
static MinecraftAccountPtr createAuthlibInjectorFromUsername(const QString &username, QString baseUrl);
|
||||
|
||||
static MinecraftAccountPtr createBlankMSA();
|
||||
|
||||
static MinecraftAccountPtr createOffline(const QString &username);
|
||||
@ -177,6 +179,10 @@ public: /* queries */
|
||||
return "msa";
|
||||
}
|
||||
break;
|
||||
case AccountType::AuthlibInjector: {
|
||||
return "authlib-injector";
|
||||
}
|
||||
break;
|
||||
case AccountType::Offline: {
|
||||
return "offline";
|
||||
}
|
||||
|
@ -27,6 +27,25 @@
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
|
||||
QString Yggdrasil::getBaseUrl()
|
||||
{
|
||||
switch (m_data->type)
|
||||
{
|
||||
case AccountType::Mojang: {
|
||||
return "https://authserver.mojang.com";
|
||||
}
|
||||
case AccountType::AuthlibInjector: {
|
||||
return m_data->authlibInjectorApiLocation + "/authserver";
|
||||
}
|
||||
// Silence warnings about unhandled enum values for values we know shouldn't be handled.
|
||||
case AccountType::MSA:
|
||||
case AccountType::Offline:
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
Yggdrasil::Yggdrasil(AccountData *data, QObject *parent)
|
||||
: AccountTask(data, parent)
|
||||
{
|
||||
@ -84,7 +103,7 @@ void Yggdrasil::refresh() {
|
||||
req.insert("requestUser", false);
|
||||
QJsonDocument doc(req);
|
||||
|
||||
QUrl reqUrl("https://authserver.mojang.com/refresh");
|
||||
QUrl reqUrl = getBaseUrl() + "/refresh";
|
||||
QByteArray requestData = doc.toJson();
|
||||
|
||||
sendRequest(reqUrl, requestData);
|
||||
@ -129,7 +148,8 @@ void Yggdrasil::login(QString password) {
|
||||
|
||||
QJsonDocument doc(req);
|
||||
|
||||
QUrl reqUrl("https://authserver.mojang.com/authenticate");
|
||||
QUrl reqUrl = getBaseUrl() + "/authenticate";
|
||||
qDebug() << "baseurl = " << getBaseUrl() << "requrl = " << reqUrl;
|
||||
QNetworkRequest netRequest(reqUrl);
|
||||
QByteArray requestData = doc.toJson();
|
||||
|
||||
|
@ -90,6 +90,7 @@ public slots:
|
||||
|
||||
private:
|
||||
void sendRequest(QUrl endpoint, QByteArray content);
|
||||
QString getBaseUrl();
|
||||
|
||||
protected:
|
||||
QNetworkReply *m_netReply = nullptr;
|
||||
|
29
launcher/minecraft/auth/flows/AuthlibInjector.cpp
Normal file
29
launcher/minecraft/auth/flows/AuthlibInjector.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include "AuthlibInjector.h"
|
||||
|
||||
#include "minecraft/auth/steps/AuthlibInjectorStep.h"
|
||||
#include "minecraft/auth/steps/MinecraftProfileStepMojang.h"
|
||||
#include "minecraft/auth/steps/YggdrasilStep.h"
|
||||
#include "minecraft/auth/steps/MinecraftProfileStep.h"
|
||||
#include "minecraft/auth/steps/MigrationEligibilityStep.h"
|
||||
#include "minecraft/auth/steps/GetSkinStep.h"
|
||||
|
||||
AuthlibInjectorRefresh::AuthlibInjectorRefresh(
|
||||
AccountData *data,
|
||||
QObject *parent
|
||||
) : AuthFlow(data, parent) {
|
||||
m_steps.append(new AuthlibInjectorStep(m_data));
|
||||
m_steps.append(new YggdrasilStep(m_data, QString()));
|
||||
m_steps.append(new MinecraftProfileStepMojang(m_data));
|
||||
m_steps.append(new GetSkinStep(m_data));
|
||||
}
|
||||
|
||||
AuthlibInjectorLogin::AuthlibInjectorLogin(
|
||||
AccountData *data,
|
||||
QString password,
|
||||
QObject *parent
|
||||
): AuthFlow(data, parent), m_password(password) {
|
||||
m_steps.append(new AuthlibInjectorStep(m_data));
|
||||
m_steps.append(new YggdrasilStep(m_data, m_password));
|
||||
m_steps.append(new MinecraftProfileStepMojang(m_data));
|
||||
m_steps.append(new GetSkinStep(m_data));
|
||||
}
|
26
launcher/minecraft/auth/flows/AuthlibInjector.h
Normal file
26
launcher/minecraft/auth/flows/AuthlibInjector.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "AuthFlow.h"
|
||||
|
||||
class AuthlibInjectorRefresh : public AuthFlow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AuthlibInjectorRefresh(
|
||||
AccountData *data,
|
||||
QObject *parent = 0
|
||||
);
|
||||
};
|
||||
|
||||
class AuthlibInjectorLogin : public AuthFlow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AuthlibInjectorLogin(
|
||||
AccountData *data,
|
||||
QString password,
|
||||
QObject *parent = 0
|
||||
);
|
||||
|
||||
private:
|
||||
QString m_password;
|
||||
};
|
58
launcher/minecraft/auth/steps/AuthlibInjectorStep.cpp
Normal file
58
launcher/minecraft/auth/steps/AuthlibInjectorStep.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
#include "AuthlibInjectorStep.h"
|
||||
#include "Application.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <QNetworkRequest>
|
||||
#include <QUuid>
|
||||
|
||||
AuthlibInjectorStep::AuthlibInjectorStep(AccountData* data) : AuthStep(data) {
|
||||
}
|
||||
|
||||
AuthlibInjectorStep::~AuthlibInjectorStep() noexcept = default;
|
||||
|
||||
QString AuthlibInjectorStep::describe() {
|
||||
return tr("Fetching authlib injector API URL");
|
||||
}
|
||||
|
||||
|
||||
void AuthlibInjectorStep::perform() {
|
||||
// Default to the same as the base URL
|
||||
QUrl url;
|
||||
url.setScheme("https");
|
||||
url.setAuthority(m_data->authlibInjectorBaseUrl);
|
||||
qDebug() << url << url.toString() << url.isLocalFile();
|
||||
m_data->authlibInjectorApiLocation = url.toString();
|
||||
QNetworkRequest request = QNetworkRequest(url);
|
||||
m_reply.reset( APPLICATION->network()->get(request));
|
||||
connect(m_reply.get(), &QNetworkReply::finished, this, &AuthlibInjectorStep::onRequestDone);
|
||||
qDebug() << "Fetching authlib injector API URL";
|
||||
}
|
||||
|
||||
void AuthlibInjectorStep::rehydrate() {
|
||||
// NOOP, for now. We only save bools and there's nothing to check.
|
||||
}
|
||||
|
||||
void AuthlibInjectorStep::onRequestDone() {
|
||||
if (m_reply->hasRawHeader("x-authlib-injector-api-location"))
|
||||
{
|
||||
QString authlibInjectorApiLocationHeader = m_reply->rawHeader("x-authlib-injector-api-location");
|
||||
QUrl url = authlibInjectorApiLocationHeader;
|
||||
if (!url.isValid())
|
||||
{
|
||||
qDebug() << "Invalid Authlib Injector API URL specified by server: " << authlibInjectorApiLocationHeader;
|
||||
emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Invalid authlib injector API URL"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_data->authlibInjectorApiLocation = authlibInjectorApiLocationHeader;
|
||||
qDebug() << "Authlib injector API URL: " << m_data->authlibInjectorApiLocation;
|
||||
emit finished(AccountTaskState::STATE_WORKING, tr("Fetched authlib injector API URL"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Authlib injector API URL not found";
|
||||
emit finished(AccountTaskState::STATE_WORKING, tr("Authlib injector API URL not found, defaulting to the supplied base URL"));
|
||||
}
|
||||
}
|
24
launcher/minecraft/auth/steps/AuthlibInjectorStep.h
Normal file
24
launcher/minecraft/auth/steps/AuthlibInjectorStep.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <QObject>
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
#include "minecraft/auth/AuthStep.h"
|
||||
|
||||
|
||||
class AuthlibInjectorStep : public AuthStep {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AuthlibInjectorStep(AccountData *data);
|
||||
virtual ~AuthlibInjectorStep() noexcept;
|
||||
|
||||
void perform() override;
|
||||
void rehydrate() override;
|
||||
|
||||
QString describe() override;
|
||||
|
||||
private slots:
|
||||
void onRequestDone();
|
||||
private:
|
||||
std::unique_ptr<QNetworkReply> m_reply;
|
||||
};
|
@ -7,7 +7,21 @@
|
||||
#include "net/NetUtils.h"
|
||||
|
||||
MinecraftProfileStep::MinecraftProfileStep(AccountData* data) : AuthStep(data) {
|
||||
|
||||
switch (m_data->type)
|
||||
{
|
||||
case AccountType::Mojang: {
|
||||
baseUrl = "https://api.minecraftservices.com";
|
||||
break;
|
||||
}
|
||||
case AccountType::AuthlibInjector: {
|
||||
baseUrl = m_data->authlibInjectorApiLocation + "/minecraftservices";
|
||||
break;
|
||||
}
|
||||
// Silence warnings about unhandled enum values for values we know shouldn't be handled.
|
||||
case AccountType::MSA:
|
||||
case AccountType::Offline:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MinecraftProfileStep::~MinecraftProfileStep() noexcept = default;
|
||||
@ -18,7 +32,7 @@ QString MinecraftProfileStep::describe() {
|
||||
|
||||
|
||||
void MinecraftProfileStep::perform() {
|
||||
auto url = QUrl("https://api.minecraftservices.com/minecraft/profile");
|
||||
QUrl url = baseUrl + "/minecraft/profile";
|
||||
QNetworkRequest request = QNetworkRequest(url);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8());
|
||||
|
@ -19,4 +19,7 @@ public:
|
||||
|
||||
private slots:
|
||||
void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
|
||||
|
||||
private:
|
||||
QString baseUrl;
|
||||
};
|
||||
|
@ -6,8 +6,24 @@
|
||||
#include "minecraft/auth/Parsers.h"
|
||||
#include "net/NetUtils.h"
|
||||
|
||||
MinecraftProfileStepMojang::MinecraftProfileStepMojang(AccountData* data) : AuthStep(data) {
|
||||
MinecraftProfileStepMojang::MinecraftProfileStepMojang(AccountData* data) : AuthStep(data) {}
|
||||
|
||||
QString MinecraftProfileStepMojang::getBaseUrl()
|
||||
{
|
||||
switch (m_data->type)
|
||||
{
|
||||
case AccountType::Mojang: {
|
||||
return "https://sessionserver.mojang.com";
|
||||
}
|
||||
case AccountType::AuthlibInjector: {
|
||||
return m_data->authlibInjectorApiLocation + "/sessionserver";
|
||||
}
|
||||
// Silence warnings about unhandled enum values for values we know shouldn't be handled.
|
||||
case AccountType::MSA:
|
||||
case AccountType::Offline:
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
MinecraftProfileStepMojang::~MinecraftProfileStepMojang() noexcept = default;
|
||||
@ -24,7 +40,7 @@ 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 = getBaseUrl() + "/session/minecraft/profile/" + m_data->minecraftProfile.id;
|
||||
QNetworkRequest req = QNetworkRequest(url);
|
||||
AuthRequest *request = new AuthRequest(this);
|
||||
connect(request, &AuthRequest::finished, this, &MinecraftProfileStepMojang::onRequestDone);
|
||||
|
@ -19,4 +19,7 @@ public:
|
||||
|
||||
private slots:
|
||||
void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
|
||||
|
||||
private:
|
||||
QString getBaseUrl();
|
||||
};
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "YggdrasilStep.h"
|
||||
|
||||
#include "minecraft/auth/AccountData.h"
|
||||
#include "minecraft/auth/AuthRequest.h"
|
||||
#include "minecraft/auth/Parsers.h"
|
||||
#include "minecraft/auth/Yggdrasil.h"
|
||||
@ -15,7 +16,14 @@ YggdrasilStep::YggdrasilStep(AccountData* data, QString password) : AuthStep(dat
|
||||
YggdrasilStep::~YggdrasilStep() noexcept = default;
|
||||
|
||||
QString YggdrasilStep::describe() {
|
||||
return tr("Logging in with Mojang account.");
|
||||
switch(m_data->type) {
|
||||
case(AccountType::Mojang):
|
||||
return tr("Logging in with Mojang account.");
|
||||
case AccountType::AuthlibInjector:
|
||||
return tr("Logging in with %1 account.").arg(m_data->authlibInjectorBaseUrl);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void YggdrasilStep::rehydrate() {
|
||||
@ -32,7 +40,9 @@ void YggdrasilStep::perform() {
|
||||
}
|
||||
|
||||
void YggdrasilStep::onAuthSucceeded() {
|
||||
emit finished(AccountTaskState::STATE_WORKING, tr("Logged in with Mojang"));
|
||||
emit m_data->type == AccountType::Mojang
|
||||
? finished(AccountTaskState::STATE_WORKING, tr("Logged in with Mojang"))
|
||||
: finished(AccountTaskState::STATE_WORKING, tr("Logged in with %1").arg(m_data->authlibInjectorBaseUrl));
|
||||
}
|
||||
|
||||
void YggdrasilStep::onAuthFailed() {
|
||||
@ -41,12 +51,32 @@ void YggdrasilStep::onAuthFailed() {
|
||||
// m_aborted = m_yggdrasil->m_aborted;
|
||||
|
||||
auto state = m_yggdrasil->taskState();
|
||||
QString errorMessage = tr("Mojang user authentication failed.");
|
||||
QString errorMessage = m_data->type == AccountType::Mojang
|
||||
? tr("Mojang user authentication failed.")
|
||||
: tr("%1 user authentication failed").arg(m_data->authlibInjectorBaseUrl);
|
||||
|
||||
// NOTE: soft error in the first step means 'offline'
|
||||
if(state == AccountTaskState::STATE_FAILED_SOFT) {
|
||||
state = AccountTaskState::STATE_OFFLINE;
|
||||
errorMessage = tr("Mojang user authentication ended with a network error.");
|
||||
switch(m_data->type) {
|
||||
case AccountType::Mojang:
|
||||
{
|
||||
errorMessage = tr("Mojang user authentication ended with a network error.");
|
||||
break;
|
||||
}
|
||||
case AccountType::AuthlibInjector:
|
||||
{
|
||||
if(m_data->authlibInjectorBaseUrl.isEmpty())
|
||||
{
|
||||
errorMessage = tr("User authentication ended with a network error, did specify a url?");
|
||||
} else {
|
||||
errorMessage = tr("%1 user authentication ended with a network error").arg(m_data->authlibInjectorBaseUrl);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
emit finished(state, errorMessage);
|
||||
}
|
||||
|
80
launcher/minecraft/launch/ConfigureAuthlibInjector.cpp
Normal file
80
launcher/minecraft/launch/ConfigureAuthlibInjector.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
#include "ConfigureAuthlibInjector.h"
|
||||
#include <launch/LaunchTask.h>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QJsonDocument>
|
||||
#include <Qt>
|
||||
|
||||
#include "Application.h"
|
||||
#include "minecraft/auth/AccountList.h"
|
||||
#include "net/ChecksumValidator.h"
|
||||
#include "net/Download.h"
|
||||
#include "net/HttpMetaCache.h"
|
||||
#include "net/NetAction.h"
|
||||
|
||||
ConfigureAuthlibInjector::ConfigureAuthlibInjector(LaunchTask* parent,
|
||||
QString authlibinjector_base_url,
|
||||
std::shared_ptr<QString> javaagent_arg)
|
||||
: LaunchStep(parent), m_javaagent_arg{ javaagent_arg }, m_authlibinjector_base_url{ authlibinjector_base_url }
|
||||
{}
|
||||
|
||||
void ConfigureAuthlibInjector::executeTask()
|
||||
{
|
||||
auto downloadFailed = [this] (QString reason) {
|
||||
return emitFailed(QString("Download failed: %1").arg(reason));
|
||||
};
|
||||
auto entry = APPLICATION->metacache()->resolveEntry("authlibinjector", "latest.json");
|
||||
|
||||
entry->setStale(true);
|
||||
m_job = std::make_unique<NetJob>("Download authlibinjector latest.json", APPLICATION->network());
|
||||
auto latestJsonDl =
|
||||
Net::Download::makeCached(QUrl("https://authlib-injector.yushi.moe/artifact/latest.json"), entry, Net::Download::Option::NoOptions);
|
||||
m_job->addNetAction(latestJsonDl);
|
||||
connect(m_job.get(), &NetJob::succeeded, this, [this, entry, downloadFailed] {
|
||||
QFile authlibInjectorLatestJson = entry->getFullPath();
|
||||
authlibInjectorLatestJson.open(QIODevice::ReadOnly);
|
||||
if (!authlibInjectorLatestJson.isOpen())
|
||||
return emitFailed(QString("Failed to open authlib-injector info json: %1").arg(authlibInjectorLatestJson.errorString()));
|
||||
|
||||
QJsonParseError json_parse_error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(authlibInjectorLatestJson.readAll(), &json_parse_error);
|
||||
if (json_parse_error.error != QJsonParseError::NoError)
|
||||
return emitFailed(QString("Failed to parse authlib-injector info json: %1").arg(json_parse_error.errorString()));
|
||||
|
||||
if (!doc.isObject())
|
||||
return emitFailed(QString("Failed to parse authlib-injector info json: not a json object"));
|
||||
QJsonObject obj = doc.object();
|
||||
|
||||
QString authlibInjectorJarUrl = obj["download_url"].toString();
|
||||
if (authlibInjectorJarUrl.isNull())
|
||||
return emitFailed(QString("Failed to parse authlib-injector info json: download url missing"));
|
||||
|
||||
QString sha256Sum = obj["checksums"].toObject()["sha256"].toString();
|
||||
if (sha256Sum.isNull())
|
||||
return emitFailed("Failed to parse authlib-injector info json: sha256 checksum missing");
|
||||
|
||||
auto sha256SumRaw = QByteArray::fromHex(sha256Sum.toLatin1());
|
||||
|
||||
QString filename = QFileInfo(authlibInjectorJarUrl).fileName();
|
||||
auto javaAgentEntry = APPLICATION->metacache()->resolveEntry("authlibinjector", filename);
|
||||
m_job = std::make_unique<NetJob>("Download authlibinjector java agent", APPLICATION->network());
|
||||
auto javaAgentDl = Net::Download::makeCached(QUrl(authlibInjectorJarUrl), javaAgentEntry, Net::Download::Option::MakeEternal);
|
||||
javaAgentDl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha256, sha256SumRaw));
|
||||
m_job->addNetAction(javaAgentDl);
|
||||
connect(m_job.get(), &NetJob::succeeded, this, [this, javaAgentEntry] {
|
||||
auto path = javaAgentEntry->getFullPath();
|
||||
qDebug() << path;
|
||||
*m_javaagent_arg = QString("%1=%2").arg(path).arg(m_authlibinjector_base_url);
|
||||
emitSucceeded();
|
||||
});
|
||||
connect(m_job.get(), &NetJob::failed, this, downloadFailed);
|
||||
m_job->start();
|
||||
},
|
||||
// This slot can't run instantly because it needs to wait for the netjob's code to stop running
|
||||
// Since it will destroy the old netjob by reassigning the unique_ptr
|
||||
Qt::QueuedConnection);
|
||||
connect(m_job.get(), &NetJob::failed, this, downloadFailed);
|
||||
m_job->start();
|
||||
}
|
||||
|
||||
void ConfigureAuthlibInjector::finalize() {}
|
24
launcher/minecraft/launch/ConfigureAuthlibInjector.h
Normal file
24
launcher/minecraft/launch/ConfigureAuthlibInjector.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <launch/LaunchStep.h>
|
||||
#include <minecraft/auth/MinecraftAccount.h>
|
||||
#include "net/NetJob.h"
|
||||
|
||||
class ConfigureAuthlibInjector: public LaunchStep
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ConfigureAuthlibInjector(LaunchTask *parent, QString authlibinjector_base_url, std::shared_ptr<QString> javaagent_arg);
|
||||
virtual ~ConfigureAuthlibInjector() {};
|
||||
|
||||
void executeTask() override;
|
||||
void finalize() override;
|
||||
bool canAbort() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
std::unique_ptr<NetJob> m_job;
|
||||
std::shared_ptr<QString> m_javaagent_arg;
|
||||
QString m_authlibinjector_base_url;
|
||||
};
|
@ -20,9 +20,10 @@
|
||||
|
||||
#include <QtWidgets/QPushButton>
|
||||
|
||||
LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent), ui(new Ui::LoginDialog)
|
||||
LoginDialog::LoginDialog(QWidget *parent, AccountType type) : QDialog(parent), ui(new Ui::LoginDialog), m_accountType{type}
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->authlibInjectorBaseTextBox->setVisible(m_accountType == AccountType::AuthlibInjector);
|
||||
ui->progressBar->setVisible(false);
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
|
||||
@ -42,7 +43,14 @@ void LoginDialog::accept()
|
||||
ui->progressBar->setVisible(true);
|
||||
|
||||
// Setup the login task and start it
|
||||
m_account = MinecraftAccount::createFromUsername(ui->userTextBox->text());
|
||||
if (m_accountType == AccountType::Mojang)
|
||||
{
|
||||
m_account = MinecraftAccount::createFromUsername(ui->userTextBox->text());
|
||||
}
|
||||
else if (m_accountType == AccountType::AuthlibInjector)
|
||||
{
|
||||
m_account = MinecraftAccount::createAuthlibInjectorFromUsername(ui->userTextBox->text(), ui->authlibInjectorBaseTextBox->text());
|
||||
}
|
||||
m_loginTask = m_account->login(ui->passTextBox->text());
|
||||
connect(m_loginTask.get(), &Task::failed, this, &LoginDialog::onTaskFailed);
|
||||
connect(m_loginTask.get(), &Task::succeeded, this, &LoginDialog::onTaskSucceeded);
|
||||
@ -106,10 +114,9 @@ void LoginDialog::onTaskProgress(qint64 current, qint64 total)
|
||||
ui->progressBar->setValue(current);
|
||||
}
|
||||
|
||||
// Public interface
|
||||
MinecraftAccountPtr LoginDialog::newAccount(QWidget *parent, QString msg)
|
||||
MinecraftAccountPtr LoginDialog::newAccount(QWidget *parent, QString msg, AccountType type)
|
||||
{
|
||||
LoginDialog dlg(parent);
|
||||
LoginDialog dlg(parent, type);
|
||||
dlg.ui->label->setText(msg);
|
||||
if (dlg.exec() == QDialog::Accepted)
|
||||
{
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <QtWidgets/QDialog>
|
||||
#include <QtCore/QEventLoop>
|
||||
|
||||
#include "minecraft/auth/AccountData.h"
|
||||
#include "minecraft/auth/MinecraftAccount.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
@ -33,10 +34,13 @@ class LoginDialog : public QDialog
|
||||
public:
|
||||
~LoginDialog();
|
||||
|
||||
static MinecraftAccountPtr newAccount(QWidget *parent, QString message);
|
||||
/*
|
||||
* @param type: Mojang or Authlib
|
||||
*/
|
||||
static MinecraftAccountPtr newAccount(QWidget *parent, QString message, AccountType type = AccountType::Mojang);
|
||||
|
||||
private:
|
||||
explicit LoginDialog(QWidget *parent = 0);
|
||||
explicit LoginDialog(QWidget *parent = 0, AccountType type = AccountType::Mojang);
|
||||
|
||||
void setUserInputsEnabled(bool enable);
|
||||
|
||||
@ -56,4 +60,5 @@ private:
|
||||
Ui::LoginDialog *ui;
|
||||
MinecraftAccountPtr m_account;
|
||||
Task::Ptr m_loginTask;
|
||||
AccountType m_accountType;
|
||||
};
|
||||
|
@ -33,6 +33,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="authlibInjectorBaseTextBox">
|
||||
<property name="placeholderText">
|
||||
<string>AuthlibInjector base URL (e.g. ely.by)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="userTextBox">
|
||||
<property name="placeholderText">
|
||||
|
@ -157,6 +157,36 @@ void AccountListPage::on_actionAddMojang_triggered()
|
||||
}
|
||||
}
|
||||
|
||||
void AccountListPage::on_actionAddAuthlibInjector_triggered()
|
||||
{
|
||||
if (!m_accounts->drmCheck()) {
|
||||
QMessageBox::warning(
|
||||
this,
|
||||
tr("Error"),
|
||||
tr(
|
||||
"You must add a Microsoft or Mojang account that owns Minecraft before you can add an Authlib Injector account."
|
||||
"<br><br>"
|
||||
"If you have lost your account you can contact Microsoft for support."
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
MinecraftAccountPtr account = LoginDialog::newAccount(
|
||||
this,
|
||||
tr("Please enter the AuthlibInjector base URL, and enter your account email and password to add your account."),
|
||||
AccountType::AuthlibInjector
|
||||
);
|
||||
|
||||
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") {
|
||||
|
@ -83,6 +83,7 @@ public:
|
||||
|
||||
public slots:
|
||||
void on_actionAddMojang_triggered();
|
||||
void on_actionAddAuthlibInjector_triggered();
|
||||
void on_actionAddMicrosoft_triggered();
|
||||
void on_actionAddOffline_triggered();
|
||||
void on_actionRemove_triggered();
|
||||
|
@ -54,6 +54,7 @@
|
||||
</attribute>
|
||||
<addaction name="actionAddMicrosoft"/>
|
||||
<addaction name="actionAddMojang"/>
|
||||
<addaction name="actionAddAuthlibInjector"/>
|
||||
<addaction name="actionAddOffline"/>
|
||||
<addaction name="actionRefresh"/>
|
||||
<addaction name="actionRemove"/>
|
||||
@ -68,6 +69,11 @@
|
||||
<string>Add &Mojang</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAddAuthlibInjector">
|
||||
<property name="text">
|
||||
<string>Add Authlib-&Injector</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRemove">
|
||||
<property name="text">
|
||||
<string>Remo&ve</string>
|
||||
|
Loading…
Reference in New Issue
Block a user