mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-08-20 09:57:09 +00:00
major refactor: auth v3
This commit is contained in:
parent
8a8b4b2573
commit
179d21d7b6
|
|
@ -3,6 +3,7 @@
|
||||||
#include "Minecraft.h"
|
#include "Minecraft.h"
|
||||||
#include "User.h"
|
#include "User.h"
|
||||||
#include "..\Minecraft.World\AuthModule.h"
|
#include "..\Minecraft.World\AuthModule.h"
|
||||||
|
#include "..\Minecraft.World\SessionAuthModule.h"
|
||||||
#include "..\Minecraft.World\HttpClient.h"
|
#include "..\Minecraft.World\HttpClient.h"
|
||||||
#include "..\Minecraft.World\StringHelpers.h"
|
#include "..\Minecraft.World\StringHelpers.h"
|
||||||
#include "Common/vendor/nlohmann/json.hpp"
|
#include "Common/vendor/nlohmann/json.hpp"
|
||||||
|
|
@ -12,24 +13,33 @@
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
static constexpr auto PROFILES_FILE = L"auth_profiles.dat";
|
static constexpr auto PROFILES_FILE = L"auth_profiles.dat";
|
||||||
static constexpr auto MS_CLIENT_ID = "00000000441cc96b";
|
static constexpr auto MS_CLIENT_ID = "00000000402b5328";
|
||||||
static json parseResponse(const HttpResponse &resp, int expectedStatus = 200);
|
static json parseResponse(const HttpResponse &resp, int expectedStatus = 200);
|
||||||
static bool msTokenExchange(const string &msAccessToken, string &mcToken, string &profId, string &profName);
|
static bool msTokenExchange(const string &msAccessToken, string &mcToken, string &profId, string &profName);
|
||||||
static bool msRefreshOAuth(const string &refreshToken, string &newAccessToken, string &newRefreshToken);
|
static bool msRefreshOAuth(const string &refreshToken, string &newAccessToken, string &newRefreshToken);
|
||||||
static bool elybyValidate(const string &accessToken, const string &clientToken);
|
static bool yggdrasilValidate(const string &accessToken, const string &clientToken, const string &validateUrl);
|
||||||
static bool elybyRefresh(const string &accessToken, const string &clientToken, string &newAccessToken, string &newClientToken);
|
static bool yggdrasilRefresh(const string &accessToken, const string &clientToken, string &newAccessToken, string &newClientToken, const string &refreshUrl);
|
||||||
|
|
||||||
vector<AuthProfile> AuthProfileManager::profiles;
|
vector<AuthProfile> AuthProfileManager::profiles;
|
||||||
int AuthProfileManager::selectedProfile = -1;
|
int AuthProfileManager::selectedProfile = -1;
|
||||||
|
|
||||||
|
static constexpr uint32_t PROFILE_MAGIC = 0x4D435032;
|
||||||
|
|
||||||
void AuthProfileManager::load()
|
void AuthProfileManager::load()
|
||||||
{
|
{
|
||||||
profiles.clear();
|
profiles.clear();
|
||||||
std::ifstream file(PROFILES_FILE, std::ios::binary);
|
std::ifstream file(PROFILES_FILE, std::ios::binary);
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
|
uint32_t header = 0;
|
||||||
|
file.read(reinterpret_cast<char *>(&header), sizeof(header));
|
||||||
|
bool hasVariation = (header == PROFILE_MAGIC);
|
||||||
|
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
file.read(reinterpret_cast<char *>(&count), sizeof(count));
|
if (hasVariation)
|
||||||
|
file.read(reinterpret_cast<char *>(&count), sizeof(count));
|
||||||
|
else
|
||||||
|
count = header;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < count && file.good(); i++)
|
for (uint32_t i = 0; i < count && file.good(); i++)
|
||||||
{
|
{
|
||||||
|
|
@ -52,6 +62,10 @@ void AuthProfileManager::load()
|
||||||
p.username = readWstr();
|
p.username = readWstr();
|
||||||
p.token = readWstr();
|
p.token = readWstr();
|
||||||
p.clientToken = readWstr();
|
p.clientToken = readWstr();
|
||||||
|
if (hasVariation)
|
||||||
|
p.variation = readWstr();
|
||||||
|
if (p.variation.empty() && p.type == AuthProfile::YGGDRASIL)
|
||||||
|
p.variation = L"elyby";
|
||||||
profiles.push_back(std::move(p));
|
profiles.push_back(std::move(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,6 +80,9 @@ void AuthProfileManager::save()
|
||||||
std::ofstream file(PROFILES_FILE, std::ios::binary | std::ios::trunc);
|
std::ofstream file(PROFILES_FILE, std::ios::binary | std::ios::trunc);
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
|
uint32_t magic = PROFILE_MAGIC;
|
||||||
|
file.write(reinterpret_cast<const char *>(&magic), sizeof(magic));
|
||||||
|
|
||||||
uint32_t count = static_cast<uint32_t>(profiles.size());
|
uint32_t count = static_cast<uint32_t>(profiles.size());
|
||||||
file.write(reinterpret_cast<const char *>(&count), sizeof(count));
|
file.write(reinterpret_cast<const char *>(&count), sizeof(count));
|
||||||
|
|
||||||
|
|
@ -83,16 +100,17 @@ void AuthProfileManager::save()
|
||||||
writeWstr(p.username);
|
writeWstr(p.username);
|
||||||
writeWstr(p.token);
|
writeWstr(p.token);
|
||||||
writeWstr(p.clientToken);
|
writeWstr(p.clientToken);
|
||||||
|
writeWstr(p.variation);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t idx = static_cast<int32_t>(selectedProfile);
|
int32_t idx = static_cast<int32_t>(selectedProfile);
|
||||||
file.write(reinterpret_cast<const char *>(&idx), sizeof(idx));
|
file.write(reinterpret_cast<const char *>(&idx), sizeof(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthProfileManager::addProfile(AuthProfile::Type type, const wstring &username, const wstring &uid, const wstring &token, const wstring &clientToken)
|
void AuthProfileManager::addProfile(AuthProfile::Type type, const wstring &username, const wstring &uid, const wstring &token, const wstring &clientToken, const wstring &variation)
|
||||||
{
|
{
|
||||||
wstring finalUid = uid.empty() ? L"offline_" + username : uid;
|
wstring finalUid = uid.empty() ? L"offline_" + username : uid;
|
||||||
profiles.push_back({type, finalUid, username, token, clientToken});
|
profiles.push_back({type, finalUid, username, token, clientToken, variation});
|
||||||
selectedProfile = static_cast<int>(profiles.size()) - 1;
|
selectedProfile = static_cast<int>(profiles.size()) - 1;
|
||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
|
|
@ -137,12 +155,14 @@ bool AuthProfileManager::applySelectedProfile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (p.type == AuthProfile::ELYBY && !p.token.empty())
|
else if (p.type == AuthProfile::YGGDRASIL && !p.token.empty())
|
||||||
{
|
{
|
||||||
if (!elybyValidate(narrowStr(p.token), narrowStr(p.clientToken)))
|
auto *provider = YggdrasilRegistry::find(p.variation);
|
||||||
|
if (!provider) provider = &YggdrasilRegistry::defaultProvider();
|
||||||
|
if (!yggdrasilValidate(narrowStr(p.token), narrowStr(p.clientToken), provider->validateUrl()))
|
||||||
{
|
{
|
||||||
string newAccess, newClient;
|
string newAccess, newClient;
|
||||||
if (elybyRefresh(narrowStr(p.token), narrowStr(p.clientToken), newAccess, newClient))
|
if (yggdrasilRefresh(narrowStr(p.token), narrowStr(p.clientToken), newAccess, newClient, provider->refreshUrl()))
|
||||||
{
|
{
|
||||||
p.token = convStringToWstring(newAccess);
|
p.token = convStringToWstring(newAccess);
|
||||||
if (!newClient.empty()) p.clientToken = convStringToWstring(newClient);
|
if (!newClient.empty()) p.clientToken = convStringToWstring(newClient);
|
||||||
|
|
@ -169,7 +189,6 @@ bool AuthProfileManager::applySelectedProfile()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- AuthFlow ---
|
|
||||||
|
|
||||||
std::thread AuthFlow::workerThread;
|
std::thread AuthFlow::workerThread;
|
||||||
std::atomic<AuthFlowState> AuthFlow::state{AuthFlowState::IDLE};
|
std::atomic<AuthFlowState> AuthFlow::state{AuthFlowState::IDLE};
|
||||||
|
|
@ -177,6 +196,7 @@ std::atomic<bool> AuthFlow::cancelRequested{false};
|
||||||
AuthResult AuthFlow::result;
|
AuthResult AuthFlow::result;
|
||||||
wstring AuthFlow::userCode;
|
wstring AuthFlow::userCode;
|
||||||
wstring AuthFlow::verificationUri;
|
wstring AuthFlow::verificationUri;
|
||||||
|
wstring AuthFlow::activeVariation;
|
||||||
|
|
||||||
void AuthFlow::reset()
|
void AuthFlow::reset()
|
||||||
{
|
{
|
||||||
|
|
@ -187,6 +207,7 @@ void AuthFlow::reset()
|
||||||
result = {};
|
result = {};
|
||||||
userCode.clear();
|
userCode.clear();
|
||||||
verificationUri.clear();
|
verificationUri.clear();
|
||||||
|
activeVariation.clear();
|
||||||
cancelRequested = false;
|
cancelRequested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,11 +218,15 @@ void AuthFlow::startMicrosoft()
|
||||||
workerThread = std::thread(microsoftFlowThread);
|
workerThread = std::thread(microsoftFlowThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthFlow::startElyBy(const wstring &username, const wstring &password)
|
void AuthFlow::startYggdrasil(const wstring &username, const wstring &password, const wstring &providerName)
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
state = AuthFlowState::EXCHANGING;
|
state = AuthFlowState::EXCHANGING;
|
||||||
workerThread = std::thread(elybyFlowThread, narrowStr(username), narrowStr(password));
|
wstring resolvedName = providerName.empty() ? YggdrasilRegistry::defaultProvider().name : providerName;
|
||||||
|
activeVariation = resolvedName;
|
||||||
|
auto *provider = YggdrasilRegistry::find(resolvedName);
|
||||||
|
string authUrl = provider ? provider->authenticateUrl() : YggdrasilRegistry::defaultProvider().authenticateUrl();
|
||||||
|
workerThread = std::thread(yggdrasilFlowThread, narrowStr(username), narrowStr(password), authUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void authFail(AuthResult &result, std::atomic<AuthFlowState> &state, const wchar_t *msg)
|
static void authFail(AuthResult &result, std::atomic<AuthFlowState> &state, const wchar_t *msg)
|
||||||
|
|
@ -210,7 +235,6 @@ static void authFail(AuthResult &result, std::atomic<AuthFlowState> &state, cons
|
||||||
state = AuthFlowState::FAILED;
|
state = AuthFlowState::FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse json response body, return discarded json on bad status
|
|
||||||
static json parseResponse(const HttpResponse &resp, int expectedStatus)
|
static json parseResponse(const HttpResponse &resp, int expectedStatus)
|
||||||
{
|
{
|
||||||
if (resp.statusCode != expectedStatus) return json::value_t::discarded;
|
if (resp.statusCode != expectedStatus) return json::value_t::discarded;
|
||||||
|
|
@ -273,19 +297,15 @@ static bool msRefreshOAuth(const string &refreshToken, string &newAccessToken, s
|
||||||
newRefreshToken = j.value("refresh_token", "");
|
newRefreshToken = j.value("refresh_token", "");
|
||||||
return !newAccessToken.empty();
|
return !newAccessToken.empty();
|
||||||
}
|
}
|
||||||
|
static bool yggdrasilValidate(const string &accessToken, const string &clientToken, const string &validateUrl)
|
||||||
// validate ely.by token via yggdrasil /validate endpoint
|
|
||||||
static bool elybyValidate(const string &accessToken, const string &clientToken)
|
|
||||||
{
|
{
|
||||||
auto resp = HttpClient::post("https://authserver.ely.by/auth/validate",
|
auto resp = HttpClient::post(validateUrl,
|
||||||
json({{"accessToken", accessToken}, {"clientToken", clientToken}}).dump());
|
json({{"accessToken", accessToken}, {"clientToken", clientToken}}).dump());
|
||||||
return resp.statusCode == 200;
|
return resp.statusCode == 200;
|
||||||
}
|
}
|
||||||
|
static bool yggdrasilRefresh(const string &accessToken, const string &clientToken, string &newAccessToken, string &newClientToken, const string &refreshUrl)
|
||||||
// refresh ely.by token via yggdrasil /refresh endpoint
|
|
||||||
static bool elybyRefresh(const string &accessToken, const string &clientToken, string &newAccessToken, string &newClientToken)
|
|
||||||
{
|
{
|
||||||
auto resp = HttpClient::post("https://authserver.ely.by/auth/refresh",
|
auto resp = HttpClient::post(refreshUrl,
|
||||||
json({{"accessToken", accessToken}, {"clientToken", clientToken}}).dump());
|
json({{"accessToken", accessToken}, {"clientToken", clientToken}}).dump());
|
||||||
|
|
||||||
auto j = parseResponse(resp);
|
auto j = parseResponse(resp);
|
||||||
|
|
@ -403,9 +423,9 @@ void AuthFlow::microsoftFlowThread()
|
||||||
state = AuthFlowState::COMPLETE;
|
state = AuthFlowState::COMPLETE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthFlow::elybyFlowThread(const string &username, const string &password)
|
void AuthFlow::yggdrasilFlowThread(const string &username, const string &password, const string &authenticateUrl)
|
||||||
{
|
{
|
||||||
auto resp = HttpClient::post("https://authserver.ely.by/auth/authenticate", json({
|
auto resp = HttpClient::post(authenticateUrl, json({
|
||||||
{"username", username},
|
{"username", username},
|
||||||
{"password", password},
|
{"password", password},
|
||||||
{"clientToken", "mcconsoles"},
|
{"clientToken", "mcconsoles"},
|
||||||
|
|
@ -416,7 +436,7 @@ void AuthFlow::elybyFlowThread(const string &username, const string &password)
|
||||||
|
|
||||||
if (resp.statusCode != 200 || respJson.is_discarded())
|
if (resp.statusCode != 200 || respJson.is_discarded())
|
||||||
{
|
{
|
||||||
string msg = "Ely.by auth failed";
|
string msg = "Yggdrasil auth failed";
|
||||||
if (!respJson.is_discarded()) msg = respJson.value("errorMessage", msg);
|
if (!respJson.is_discarded()) msg = respJson.value("errorMessage", msg);
|
||||||
result = {false, {}, {}, {}, {}, convStringToWstring(msg)};
|
result = {false, {}, {}, {}, {}, convStringToWstring(msg)};
|
||||||
state = AuthFlowState::FAILED;
|
state = AuthFlowState::FAILED;
|
||||||
|
|
@ -424,16 +444,16 @@ void AuthFlow::elybyFlowThread(const string &username, const string &password)
|
||||||
}
|
}
|
||||||
|
|
||||||
string accessToken = respJson.value("accessToken", "");
|
string accessToken = respJson.value("accessToken", "");
|
||||||
string elyClientToken = respJson.value("clientToken", "");
|
string yggClientToken = respJson.value("clientToken", "");
|
||||||
string uuid, name;
|
string uuid, name;
|
||||||
try { uuid = respJson["selectedProfile"].value("id", ""); name = respJson["selectedProfile"].value("name", ""); } catch (...) {}
|
try { uuid = respJson["selectedProfile"].value("id", ""); name = respJson["selectedProfile"].value("name", ""); } catch (...) {}
|
||||||
|
|
||||||
if (accessToken.empty() || uuid.empty() || name.empty())
|
if (accessToken.empty() || uuid.empty() || name.empty())
|
||||||
{
|
{
|
||||||
authFail(result, state, L"Ely.by response missing profile");
|
authFail(result, state, L"Yggdrasil response missing profile");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = {true, convStringToWstring(name), convStringToWstring(uuid), convStringToWstring(accessToken), convStringToWstring(elyClientToken), {}};
|
result = {true, convStringToWstring(name), convStringToWstring(uuid), convStringToWstring(accessToken), convStringToWstring(yggClientToken), {}};
|
||||||
state = AuthFlowState::COMPLETE;
|
state = AuthFlowState::COMPLETE;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,14 @@ using namespace std;
|
||||||
|
|
||||||
struct AuthProfile
|
struct AuthProfile
|
||||||
{
|
{
|
||||||
enum Type : uint8_t { MICROSOFT, ELYBY, OFFLINE };
|
enum Type : uint8_t { MICROSOFT, YGGDRASIL, OFFLINE };
|
||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
wstring uid;
|
wstring uid;
|
||||||
wstring username;
|
wstring username;
|
||||||
wstring token;
|
wstring token;
|
||||||
wstring clientToken;
|
wstring clientToken;
|
||||||
|
wstring variation;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AuthProfileManager
|
class AuthProfileManager
|
||||||
|
|
@ -23,7 +24,7 @@ private:
|
||||||
public:
|
public:
|
||||||
static void load();
|
static void load();
|
||||||
static void save();
|
static void save();
|
||||||
static void addProfile(AuthProfile::Type type, const wstring &username, const wstring &uid = L"", const wstring &token = L"", const wstring &clientToken = L"");
|
static void addProfile(AuthProfile::Type type, const wstring &username, const wstring &uid = L"", const wstring &token = L"", const wstring &clientToken = L"", const wstring &variation = L"");
|
||||||
static void removeSelectedProfile();
|
static void removeSelectedProfile();
|
||||||
static bool applySelectedProfile();
|
static bool applySelectedProfile();
|
||||||
|
|
||||||
|
|
@ -60,17 +61,17 @@ private:
|
||||||
static AuthResult result;
|
static AuthResult result;
|
||||||
static wstring userCode;
|
static wstring userCode;
|
||||||
static wstring verificationUri;
|
static wstring verificationUri;
|
||||||
|
static wstring activeVariation;
|
||||||
static void microsoftFlowThread();
|
static void microsoftFlowThread();
|
||||||
static void elybyFlowThread(const string &username, const string &password);
|
static void yggdrasilFlowThread(const string &username, const string &password, const string &authenticateUrl);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void startMicrosoft();
|
static void startMicrosoft();
|
||||||
static void startElyBy(const wstring &username, const wstring &password);
|
static void startYggdrasil(const wstring &username, const wstring &password, const wstring &providerName = L"");
|
||||||
|
|
||||||
static AuthFlowState getState() { return state.load(); }
|
static AuthFlowState getState() { return state.load(); }
|
||||||
static const AuthResult &getResult() { return result; }
|
static const AuthResult &getResult() { return result; }
|
||||||
static const wstring &getUserCode() { return userCode; }
|
static const wstring &getUserCode() { return userCode; }
|
||||||
static const wstring &getVerificationUri() { return verificationUri; }
|
static const wstring &getVerificationUri() { return verificationUri; }
|
||||||
|
static const wstring &getActiveVariation() { return activeVariation; }
|
||||||
static void reset();
|
static void reset();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ set_source_files_properties(compat_shims.cpp PROPERTIES SKIP_PRECOMPILE_HEADERS
|
||||||
|
|
||||||
configure_compiler_target(Minecraft.Client)
|
configure_compiler_target(Minecraft.Client)
|
||||||
|
|
||||||
|
target_link_options(Minecraft.Client PRIVATE "/MAP")
|
||||||
|
|
||||||
set_target_properties(Minecraft.Client PROPERTIES
|
set_target_properties(Minecraft.Client PROPERTIES
|
||||||
OUTPUT_NAME "Minecraft.Client"
|
OUTPUT_NAME "Minecraft.Client"
|
||||||
VS_DEBUGGER_WORKING_DIRECTORY "$<TARGET_FILE_DIR:Minecraft.Client>"
|
VS_DEBUGGER_WORKING_DIRECTORY "$<TARGET_FILE_DIR:Minecraft.Client>"
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,6 @@
|
||||||
#include "DLCTexturePack.h"
|
#include "DLCTexturePack.h"
|
||||||
#include "..\Minecraft.World\HandshakeManager.h"
|
#include "..\Minecraft.World\HandshakeManager.h"
|
||||||
#include "..\Minecraft.World\SessionAuthModule.h"
|
#include "..\Minecraft.World\SessionAuthModule.h"
|
||||||
#include "..\Minecraft.World\OfflineAuthModule.h"
|
|
||||||
#include "AuthScreen.h"
|
#include "AuthScreen.h"
|
||||||
|
|
||||||
#ifdef _WINDOWS64
|
#ifdef _WINDOWS64
|
||||||
|
|
@ -4141,21 +4140,33 @@ ClientConnection::DeferredEntityLinkPacket::DeferredEntityLinkPacket(shared_ptr<
|
||||||
|
|
||||||
void ClientConnection::beginAuth()
|
void ClientConnection::beginAuth()
|
||||||
{
|
{
|
||||||
|
app.DebugPrintf("AUTH: beginAuth() starting\n");
|
||||||
handshakeManager = new HandshakeManager(false);
|
handshakeManager = new HandshakeManager(false);
|
||||||
handshakeManager->registerModule(std::make_unique<SessionAuthModule>());
|
|
||||||
handshakeManager->registerModule(std::make_unique<KeypairOfflineAuthModule>());
|
|
||||||
handshakeManager->registerModule(std::make_unique<OfflineAuthModule>());
|
|
||||||
|
|
||||||
const auto &profiles = AuthProfileManager::getProfiles();
|
const auto &profiles = AuthProfileManager::getProfiles();
|
||||||
int idx = AuthProfileManager::getSelectedIndex();
|
int idx = AuthProfileManager::getSelectedIndex();
|
||||||
|
bool isOffline = true;
|
||||||
if (idx >= 0 && idx < static_cast<int>(profiles.size()))
|
if (idx >= 0 && idx < static_cast<int>(profiles.size()))
|
||||||
{
|
{
|
||||||
const auto &p = profiles[idx];
|
const auto &p = profiles[idx];
|
||||||
|
isOffline = (p.type == AuthProfile::OFFLINE);
|
||||||
wstring variation;
|
wstring variation;
|
||||||
if (p.type == AuthProfile::MICROSOFT) variation = L"mojang";
|
if (p.type == AuthProfile::MICROSOFT) variation = L"mojang";
|
||||||
else if (p.type == AuthProfile::ELYBY) variation = L"elyby";
|
else if (p.type == AuthProfile::YGGDRASIL) variation = p.variation;
|
||||||
|
app.DebugPrintf("AUTH: profile type=%d variation=%ls username=%ls\n",
|
||||||
|
(int)p.type, variation.c_str(), p.username.c_str());
|
||||||
handshakeManager->setCredentials(p.token, p.uid, p.username, variation);
|
handshakeManager->setCredentials(p.token, p.uid, p.username, variation);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.DebugPrintf("AUTH: no valid profile selected (idx=%d, count=%d)\n",
|
||||||
|
idx, (int)profiles.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isOffline)
|
||||||
|
handshakeManager->registerModule(std::make_unique<SessionAuthModule>());
|
||||||
|
handshakeManager->registerModule(std::make_unique<KeypairOfflineAuthModule>());
|
||||||
|
handshakeManager->registerModule(std::make_unique<OfflineAuthModule>());
|
||||||
|
|
||||||
auto initial = handshakeManager->createInitialPacket();
|
auto initial = handshakeManager->createInitialPacket();
|
||||||
if (initial) send(initial);
|
if (initial) send(initial);
|
||||||
|
|
|
||||||
|
|
@ -413,6 +413,35 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!g_NetworkManager.IsHost())
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
connection->beginAuth();
|
||||||
|
while (!connection->authComplete && !connection->isClosed() && IsInSession() && !g_NetworkManager.IsLeavingGame())
|
||||||
|
{
|
||||||
|
connection->tick();
|
||||||
|
Sleep(50);
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
app.DebugPrintf("AUTH: exception during auth handshake\n");
|
||||||
|
connection->close();
|
||||||
|
delete connection;
|
||||||
|
connection = nullptr;
|
||||||
|
MinecraftServer::HaltServer();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!connection->authComplete)
|
||||||
|
{
|
||||||
|
app.DebugPrintf("Auth handshake did not complete, aborting connection\n");
|
||||||
|
connection->close();
|
||||||
|
delete connection;
|
||||||
|
connection = nullptr;
|
||||||
|
MinecraftServer::HaltServer();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
connection->send(std::make_shared<PreLoginPacket>(minecraft->user->name));
|
connection->send(std::make_shared<PreLoginPacket>(minecraft->user->name));
|
||||||
|
|
||||||
// Tick connection until we're ready to go. The stages involved in this are:
|
// Tick connection until we're ready to go. The stages involved in this are:
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include "..\..\..\Minecraft.World\Mth.h"
|
#include "..\..\..\Minecraft.World\Mth.h"
|
||||||
#include "..\..\..\Minecraft.World\StringHelpers.h"
|
#include "..\..\..\Minecraft.World\StringHelpers.h"
|
||||||
#include "..\..\..\Minecraft.World\Random.h"
|
#include "..\..\..\Minecraft.World\Random.h"
|
||||||
|
#include "..\..\..\Minecraft.World\SessionAuthModule.h"
|
||||||
#include "..\..\User.h"
|
#include "..\..\User.h"
|
||||||
#include "..\..\MinecraftServer.h"
|
#include "..\..\MinecraftServer.h"
|
||||||
#include "..\..\AuthScreen.h"
|
#include "..\..\AuthScreen.h"
|
||||||
|
|
@ -459,7 +460,7 @@ void UIScene_MainMenu::RunAction(int iPad)
|
||||||
|
|
||||||
static int s_authPad = 0;
|
static int s_authPad = 0;
|
||||||
static void *s_authParam = nullptr;
|
static void *s_authParam = nullptr;
|
||||||
static wstring s_elybyUsername;
|
static wstring s_yggdrasilUsername;
|
||||||
static bool s_authFlowActive = false;
|
static bool s_authFlowActive = false;
|
||||||
|
|
||||||
static wstring ReadKeyboardText(int maxLen)
|
static wstring ReadKeyboardText(int maxLen)
|
||||||
|
|
@ -509,11 +510,17 @@ static const wchar_t *BuildAuthProfileText()
|
||||||
for (int i = 0; i < static_cast<int>(profiles.size()); i++)
|
for (int i = 0; i < static_cast<int>(profiles.size()); i++)
|
||||||
{
|
{
|
||||||
const auto &p = profiles[i];
|
const auto &p = profiles[i];
|
||||||
const wchar_t *type = (p.type == AuthProfile::MICROSOFT) ? L"[MS]"
|
wstring label;
|
||||||
: (p.type == AuthProfile::ELYBY) ? L"[Ely]" : L"[Off]";
|
if (p.type == AuthProfile::MICROSOFT) label = L"[MS]";
|
||||||
|
else if (p.type == AuthProfile::YGGDRASIL)
|
||||||
|
{
|
||||||
|
auto *provider = YggdrasilRegistry::find(p.variation);
|
||||||
|
label = provider ? L"[" + provider->displayName + L"]" : L"[Ygg]";
|
||||||
|
}
|
||||||
|
else label = L"[Off]";
|
||||||
if (i > 0) text += L"\n";
|
if (i > 0) text += L"\n";
|
||||||
text += (i == sel) ? L"> " : L" ";
|
text += (i == sel) ? L"> " : L" ";
|
||||||
text += wstring(type) + L" " + p.username;
|
text += label + L" " + p.username;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return text.c_str();
|
return text.c_str();
|
||||||
|
|
@ -531,7 +538,9 @@ void UIScene_MainMenu::ShowAuthMenu(int iPad, void *pClass)
|
||||||
|
|
||||||
void UIScene_MainMenu::ShowAuthAddMenu(int iPad, void *pClass)
|
void UIScene_MainMenu::ShowAuthAddMenu(int iPad, void *pClass)
|
||||||
{
|
{
|
||||||
static const wchar_t *addOptions[] = { L"Microsoft Auth", L"Ely.by Auth", L"Add Offline User", L"Back" };
|
static wstring yggLabel;
|
||||||
|
yggLabel = YggdrasilRegistry::defaultProvider().displayName + L" Auth";
|
||||||
|
const wchar_t *addOptions[] = { L"Microsoft Auth", yggLabel.c_str(), L"Add Offline User", L"Back" };
|
||||||
ShowAuthMessageBox(iPad, L"Add Profile", L"Select an auth type",
|
ShowAuthMessageBox(iPad, L"Add Profile", L"Select an auth type",
|
||||||
addOptions, 4, &UIScene_MainMenu::AuthAddMenuReturned, pClass);
|
addOptions, 4, &UIScene_MainMenu::AuthAddMenuReturned, pClass);
|
||||||
}
|
}
|
||||||
|
|
@ -602,15 +611,15 @@ int UIScene_MainMenu::AuthAddMenuReturned(LPVOID lpParam, int iPad, const C4JSto
|
||||||
{
|
{
|
||||||
#ifdef _WINDOWS64
|
#ifdef _WINDOWS64
|
||||||
UIKeyboardInitData kbData;
|
UIKeyboardInitData kbData;
|
||||||
kbData.title = L"Ely.by Username";
|
kbData.title = L"Username";
|
||||||
kbData.defaultText = L"";
|
kbData.defaultText = L"";
|
||||||
kbData.maxChars = 64;
|
kbData.maxChars = 64;
|
||||||
kbData.callback = &UIScene_MainMenu::ElyByUsernameReturned;
|
kbData.callback = &UIScene_MainMenu::YggdrasilUsernameReturned;
|
||||||
kbData.lpParam = lpParam;
|
kbData.lpParam = lpParam;
|
||||||
kbData.pcMode = g_KBMInput.IsKBMActive();
|
kbData.pcMode = g_KBMInput.IsKBMActive();
|
||||||
ui.NavigateToScene(iPad, eUIScene_Keyboard, &kbData);
|
ui.NavigateToScene(iPad, eUIScene_Keyboard, &kbData);
|
||||||
#else
|
#else
|
||||||
InputManager.RequestKeyboard(L"Ely.by Username", L"", (DWORD)0, 64, &UIScene_MainMenu::ElyByUsernameReturned, lpParam, C_4JInput::EKeyboardMode_Default);
|
InputManager.RequestKeyboard(L"Username", L"", (DWORD)0, 64, &UIScene_MainMenu::YggdrasilUsernameReturned, lpParam, C_4JInput::EKeyboardMode_Default);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -646,26 +655,26 @@ int UIScene_MainMenu::AuthMsFlowReturned(LPVOID lpParam, int iPad, const C4JStor
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int UIScene_MainMenu::ElyByUsernameReturned(LPVOID lpParam, const bool bRes)
|
int UIScene_MainMenu::YggdrasilUsernameReturned(LPVOID lpParam, const bool bRes)
|
||||||
{
|
{
|
||||||
if (bRes)
|
if (bRes)
|
||||||
{
|
{
|
||||||
wstring name = ReadKeyboardText(128);
|
wstring name = ReadKeyboardText(128);
|
||||||
if (!name.empty())
|
if (!name.empty())
|
||||||
{
|
{
|
||||||
s_elybyUsername = std::move(name);
|
s_yggdrasilUsername = std::move(name);
|
||||||
#ifdef _WINDOWS64
|
#ifdef _WINDOWS64
|
||||||
UIKeyboardInitData kbData;
|
UIKeyboardInitData kbData;
|
||||||
kbData.title = L"Ely.by Password";
|
kbData.title = L"Password";
|
||||||
kbData.defaultText = L"";
|
kbData.defaultText = L"";
|
||||||
kbData.maxChars = 128;
|
kbData.maxChars = 128;
|
||||||
kbData.callback = &UIScene_MainMenu::ElyByPasswordReturned;
|
kbData.callback = &UIScene_MainMenu::YggdrasilPasswordReturned;
|
||||||
kbData.lpParam = lpParam;
|
kbData.lpParam = lpParam;
|
||||||
kbData.pcMode = g_KBMInput.IsKBMActive();
|
kbData.pcMode = g_KBMInput.IsKBMActive();
|
||||||
kbData.keyboardMode = C_4JInput::EKeyboardMode_Password;
|
kbData.keyboardMode = C_4JInput::EKeyboardMode_Password;
|
||||||
ui.NavigateToScene(s_authPad, eUIScene_Keyboard, &kbData);
|
ui.NavigateToScene(s_authPad, eUIScene_Keyboard, &kbData);
|
||||||
#else
|
#else
|
||||||
InputManager.RequestKeyboard(L"Ely.by Password", L"", (DWORD)0, 128, &UIScene_MainMenu::ElyByPasswordReturned, lpParam, C_4JInput::EKeyboardMode_Password);
|
InputManager.RequestKeyboard(L"Password", L"", (DWORD)0, 128, &UIScene_MainMenu::YggdrasilPasswordReturned, lpParam, C_4JInput::EKeyboardMode_Password);
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -674,18 +683,20 @@ int UIScene_MainMenu::ElyByUsernameReturned(LPVOID lpParam, const bool bRes)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int UIScene_MainMenu::ElyByPasswordReturned(LPVOID lpParam, const bool bRes)
|
int UIScene_MainMenu::YggdrasilPasswordReturned(LPVOID lpParam, const bool bRes)
|
||||||
{
|
{
|
||||||
if (bRes)
|
if (bRes)
|
||||||
{
|
{
|
||||||
wstring pass = ReadKeyboardText(256);
|
wstring pass = ReadKeyboardText(256);
|
||||||
if (!pass.empty())
|
if (!pass.empty())
|
||||||
{
|
{
|
||||||
AuthFlow::startElyBy(s_elybyUsername, pass);
|
AuthFlow::startYggdrasil(s_yggdrasilUsername, pass);
|
||||||
s_authFlowActive = true;
|
s_authFlowActive = true;
|
||||||
|
|
||||||
|
static wstring loginTitle;
|
||||||
|
loginTitle = YggdrasilRegistry::defaultProvider().displayName + L" Login";
|
||||||
static const wchar_t *waitOptions[] = { L"Cancel" };
|
static const wchar_t *waitOptions[] = { L"Cancel" };
|
||||||
ShowAuthMessageBox(s_authPad, L"Ely.by Login", L"Authenticating...",
|
ShowAuthMessageBox(s_authPad, loginTitle.c_str(), L"Authenticating...",
|
||||||
waitOptions, 1, &UIScene_MainMenu::AuthMsFlowReturned, lpParam, true);
|
waitOptions, 1, &UIScene_MainMenu::AuthMsFlowReturned, lpParam, true);
|
||||||
|
|
||||||
SecureZeroMemory(pass.data(), pass.size() * sizeof(wchar_t));
|
SecureZeroMemory(pass.data(), pass.size() * sizeof(wchar_t));
|
||||||
|
|
@ -2144,8 +2155,9 @@ void UIScene_MainMenu::tick()
|
||||||
{
|
{
|
||||||
s_authFlowActive = false;
|
s_authFlowActive = false;
|
||||||
const auto &r = AuthFlow::getResult();
|
const auto &r = AuthFlow::getResult();
|
||||||
auto type = AuthFlow::getUserCode().empty() ? AuthProfile::ELYBY : AuthProfile::MICROSOFT;
|
auto type = AuthFlow::getUserCode().empty() ? AuthProfile::YGGDRASIL : AuthProfile::MICROSOFT;
|
||||||
AuthProfileManager::addProfile(type, r.username, r.uuid, r.accessToken, r.clientToken);
|
wstring variation = (type == AuthProfile::YGGDRASIL) ? AuthFlow::getActiveVariation() : L"";
|
||||||
|
AuthProfileManager::addProfile(type, r.username, r.uuid, r.accessToken, r.clientToken, variation);
|
||||||
AuthFlow::reset();
|
AuthFlow::reset();
|
||||||
if (scene) ui.NavigateBack(s_authPad);
|
if (scene) ui.NavigateBack(s_authPad);
|
||||||
ShowAuthMenu(s_authPad, s_authParam);
|
ShowAuthMenu(s_authPad, s_authParam);
|
||||||
|
|
|
||||||
|
|
@ -151,8 +151,8 @@ private:
|
||||||
static int AuthAddMenuReturned(void *pParam, int iPad, C4JStorage::EMessageResult result);
|
static int AuthAddMenuReturned(void *pParam, int iPad, C4JStorage::EMessageResult result);
|
||||||
static int AuthKeyboardReturned(LPVOID lpParam, const bool bRes);
|
static int AuthKeyboardReturned(LPVOID lpParam, const bool bRes);
|
||||||
static int AuthMsFlowReturned(void *pParam, int iPad, C4JStorage::EMessageResult result);
|
static int AuthMsFlowReturned(void *pParam, int iPad, C4JStorage::EMessageResult result);
|
||||||
static int ElyByUsernameReturned(LPVOID lpParam, const bool bRes);
|
static int YggdrasilUsernameReturned(LPVOID lpParam, const bool bRes);
|
||||||
static int ElyByPasswordReturned(LPVOID lpParam, const bool bRes);
|
static int YggdrasilPasswordReturned(LPVOID lpParam, const bool bRes);
|
||||||
|
|
||||||
static void LoadTrial();
|
static void LoadTrial();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "..\Minecraft.World\HandshakeManager.h"
|
#include "..\Minecraft.World\HandshakeManager.h"
|
||||||
#include "..\Minecraft.World\SessionAuthModule.h"
|
#include "..\Minecraft.World\SessionAuthModule.h"
|
||||||
#include "..\Minecraft.World\OfflineAuthModule.h"
|
|
||||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||||
#include "..\Minecraft.Server\ServerLogManager.h"
|
#include "..\Minecraft.Server\ServerLogManager.h"
|
||||||
#include "..\Minecraft.Server\Access\Access.h"
|
#include "..\Minecraft.Server\Access\Access.h"
|
||||||
|
|
@ -105,8 +104,14 @@ void PendingConnection::disconnect(DisconnectPacket::eDisconnectReason reason)
|
||||||
|
|
||||||
void PendingConnection::handlePreLogin(shared_ptr<PreLoginPacket> packet)
|
void PendingConnection::handlePreLogin(shared_ptr<PreLoginPacket> packet)
|
||||||
{
|
{
|
||||||
if (handshakeManager && !authComplete)
|
if (!authComplete)
|
||||||
{
|
{
|
||||||
|
if (!handshakeManager)
|
||||||
|
{
|
||||||
|
app.DebugPrintf("PendingConnection: PreLogin received without auth handshake, disconnecting\n");
|
||||||
|
disconnect(DisconnectPacket::eDisconnect_AuthFailed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
app.DebugPrintf("PendingConnection: PreLogin received before auth complete, disconnecting\n");
|
app.DebugPrintf("PendingConnection: PreLogin received before auth complete, disconnecting\n");
|
||||||
disconnect(DisconnectPacket::eDisconnect_Closed);
|
disconnect(DisconnectPacket::eDisconnect_Closed);
|
||||||
return;
|
return;
|
||||||
|
|
@ -413,10 +418,12 @@ bool PendingConnection::isDisconnected()
|
||||||
void PendingConnection::initAuth()
|
void PendingConnection::initAuth()
|
||||||
{
|
{
|
||||||
handshakeManager = new HandshakeManager(true);
|
handshakeManager = new HandshakeManager(true);
|
||||||
if (server->authMode == "session")
|
handshakeManager->registerModule(std::make_unique<SessionAuthModule>());
|
||||||
handshakeManager->registerModule(std::make_unique<SessionAuthModule>());
|
if (server->authMode != "session")
|
||||||
else
|
{
|
||||||
|
handshakeManager->registerModule(std::make_unique<KeypairOfflineAuthModule>());
|
||||||
handshakeManager->registerModule(std::make_unique<OfflineAuthModule>());
|
handshakeManager->registerModule(std::make_unique<OfflineAuthModule>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PendingConnection::handleAuth(const shared_ptr<AuthPacket> &packet)
|
void PendingConnection::handleAuth(const shared_ptr<AuthPacket> &packet)
|
||||||
|
|
|
||||||
|
|
@ -1296,6 +1296,34 @@ static Minecraft* InitialiseMinecraftRuntime()
|
||||||
return pMinecraft;
|
return pMinecraft;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LONG WINAPI CrashHandler(EXCEPTION_POINTERS *ep)
|
||||||
|
{
|
||||||
|
auto *record = ep->ExceptionRecord;
|
||||||
|
auto *ctx = ep->ContextRecord;
|
||||||
|
HMODULE hMod = GetModuleHandleA(nullptr);
|
||||||
|
|
||||||
|
wchar_t buf[1024];
|
||||||
|
swprintf(buf, 1024,
|
||||||
|
L"Unhandled exception!\n\nCode: 0x%08X\nAddress: 0x%p\nBase: 0x%p\nRVA: 0x%llX\n\nPlease report this crash info.",
|
||||||
|
record->ExceptionCode, record->ExceptionAddress, hMod,
|
||||||
|
(unsigned long long)((char *)record->ExceptionAddress - (char *)hMod));
|
||||||
|
|
||||||
|
FILE *clog = nullptr;
|
||||||
|
if (fopen_s(&clog, "crash.log", "w") == 0 && clog)
|
||||||
|
{
|
||||||
|
fprintf(clog, "[CRASH] Code=0x%08X Addr=0x%p Base=0x%p RVA=0x%llX\n",
|
||||||
|
record->ExceptionCode, record->ExceptionAddress, hMod,
|
||||||
|
(unsigned long long)((char *)record->ExceptionAddress - (char *)hMod));
|
||||||
|
fprintf(clog, " RIP=0x%llX RSP=0x%llX RBP=0x%llX\n", ctx->Rip, ctx->Rsp, ctx->Rbp);
|
||||||
|
fprintf(clog, " RAX=0x%llX RBX=0x%llX RCX=0x%llX\n", ctx->Rax, ctx->Rbx, ctx->Rcx);
|
||||||
|
fprintf(clog, " RDX=0x%llX RSI=0x%llX RDI=0x%llX\n", ctx->Rdx, ctx->Rsi, ctx->Rdi);
|
||||||
|
fclose(clog);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageBoxW(nullptr, buf, L"MinecraftConsoles Crash", MB_OK | MB_ICONERROR);
|
||||||
|
return EXCEPTION_EXECUTE_HANDLER;
|
||||||
|
}
|
||||||
|
|
||||||
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||||
_In_opt_ HINSTANCE hPrevInstance,
|
_In_opt_ HINSTANCE hPrevInstance,
|
||||||
_In_ LPTSTR lpCmdLine,
|
_In_ LPTSTR lpCmdLine,
|
||||||
|
|
@ -1304,6 +1332,8 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||||
UNREFERENCED_PARAMETER(hPrevInstance);
|
UNREFERENCED_PARAMETER(hPrevInstance);
|
||||||
UNREFERENCED_PARAMETER(lpCmdLine);
|
UNREFERENCED_PARAMETER(lpCmdLine);
|
||||||
|
|
||||||
|
SetUnhandledExceptionFilter(CrashHandler);
|
||||||
|
|
||||||
// 4J-Win64: set CWD to exe dir so asset paths resolve correctly
|
// 4J-Win64: set CWD to exe dir so asset paths resolve correctly
|
||||||
{
|
{
|
||||||
char szExeDir[MAX_PATH] = {};
|
char szExeDir[MAX_PATH] = {};
|
||||||
|
|
|
||||||
|
|
@ -25,3 +25,21 @@ public:
|
||||||
protected:
|
protected:
|
||||||
bool extractIdentity(const vector<pair<wstring, wstring>> &fields, wstring &outUid, wstring &outUsername);
|
bool extractIdentity(const vector<pair<wstring, wstring>> &fields, wstring &outUid, wstring &outUsername);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class KeypairOfflineAuthModule : public AuthModule
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const wchar_t *schemeName() override { return L"mcconsoles:keypair_offline"; }
|
||||||
|
vector<wstring> supportedVariations() override { return {L"rsa2048", L"ed25519"}; }
|
||||||
|
vector<pair<wstring, wstring>> getSettings(const wstring &variation) override { return {{L"key_type", variation}}; }
|
||||||
|
bool onAuthData(const vector<pair<wstring, wstring>> &fields, wstring &outUid, wstring &outUsername) override { return extractIdentity(fields, outUid, outUsername); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class OfflineAuthModule : public AuthModule
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const wchar_t *schemeName() override { return L"mcconsoles:offline"; }
|
||||||
|
vector<wstring> supportedVariations() override { return {}; }
|
||||||
|
vector<pair<wstring, wstring>> getSettings(const wstring &) override { return {}; }
|
||||||
|
bool onAuthData(const vector<pair<wstring, wstring>> &fields, wstring &outUid, wstring &outUsername) override { return extractIdentity(fields, outUid, outUsername); }
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,16 @@ shared_ptr<AuthPacket> HandshakeManager::handlePacket(const shared_ptr<AuthPacke
|
||||||
shared_ptr<AuthPacket> HandshakeManager::createInitialPacket()
|
shared_ptr<AuthPacket> HandshakeManager::createInitialPacket()
|
||||||
{
|
{
|
||||||
state = HandshakeState::VERSION_SENT;
|
state = HandshakeState::VERSION_SENT;
|
||||||
return makePacket(AuthStage::ANNOUNCE_VERSION, {{L"version", PROTOCOL_VERSION}});
|
wstring schemes;
|
||||||
|
for (const auto &[name, mod] : modules)
|
||||||
|
{
|
||||||
|
if (!schemes.empty()) schemes += L",";
|
||||||
|
schemes += name;
|
||||||
|
}
|
||||||
|
return makePacket(AuthStage::ANNOUNCE_VERSION, {
|
||||||
|
{L"version", PROTOCOL_VERSION},
|
||||||
|
{L"schemes", schemes}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<AuthPacket> HandshakeManager::handleServer(const shared_ptr<AuthPacket> &packet)
|
shared_ptr<AuthPacket> HandshakeManager::handleServer(const shared_ptr<AuthPacket> &packet)
|
||||||
|
|
@ -61,11 +70,32 @@ shared_ptr<AuthPacket> HandshakeManager::handleServer(const shared_ptr<AuthPacke
|
||||||
if (protocolVersion != PROTOCOL_VERSION)
|
if (protocolVersion != PROTOCOL_VERSION)
|
||||||
return fail();
|
return fail();
|
||||||
|
|
||||||
// Pick first registered module as the scheme
|
|
||||||
if (modules.empty())
|
if (modules.empty())
|
||||||
return fail();
|
return fail();
|
||||||
|
|
||||||
activeModule = modules.begin()->second.get();
|
auto splitCsv = [](const wstring &s) {
|
||||||
|
vector<wstring> out;
|
||||||
|
for (size_t p = 0; p < s.size(); ) {
|
||||||
|
size_t c = s.find(L',', p);
|
||||||
|
if (c == wstring::npos) c = s.size();
|
||||||
|
out.push_back(s.substr(p, c - p));
|
||||||
|
p = c + 1;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
auto supported = splitCsv(getField(packet->fields, L"schemes"));
|
||||||
|
|
||||||
|
activeModule = nullptr;
|
||||||
|
for (auto &[name, mod] : modules)
|
||||||
|
{
|
||||||
|
if (supported.empty() || std::find(supported.begin(), supported.end(), name) != supported.end())
|
||||||
|
{
|
||||||
|
activeModule = mod.get();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!activeModule)
|
||||||
|
return fail();
|
||||||
state = HandshakeState::SCHEME_DECLARED;
|
state = HandshakeState::SCHEME_DECLARED;
|
||||||
return makePacket(AuthStage::DECLARE_SCHEME, {
|
return makePacket(AuthStage::DECLARE_SCHEME, {
|
||||||
{L"version", PROTOCOL_VERSION},
|
{L"version", PROTOCOL_VERSION},
|
||||||
|
|
@ -133,6 +163,8 @@ shared_ptr<AuthPacket> HandshakeManager::handleClient(const shared_ptr<AuthPacke
|
||||||
protocolVersion = getField(packet->fields, L"version");
|
protocolVersion = getField(packet->fields, L"version");
|
||||||
wstring scheme = getField(packet->fields, L"scheme");
|
wstring scheme = getField(packet->fields, L"scheme");
|
||||||
|
|
||||||
|
app.DebugPrintf("AUTH CLIENT: DECLARE_SCHEME scheme=%ls\n", scheme.c_str());
|
||||||
|
|
||||||
if (protocolVersion != PROTOCOL_VERSION)
|
if (protocolVersion != PROTOCOL_VERSION)
|
||||||
return fail();
|
return fail();
|
||||||
|
|
||||||
|
|
@ -149,24 +181,39 @@ shared_ptr<AuthPacket> HandshakeManager::handleClient(const shared_ptr<AuthPacke
|
||||||
else
|
else
|
||||||
activeVariation = variations.empty() ? L"" : variations[0];
|
activeVariation = variations.empty() ? L"" : variations[0];
|
||||||
|
|
||||||
|
app.DebugPrintf("AUTH CLIENT: accepting variation=%ls\n", activeVariation.c_str());
|
||||||
state = HandshakeState::SCHEME_ACCEPTED;
|
state = HandshakeState::SCHEME_ACCEPTED;
|
||||||
return makePacket(AuthStage::ACCEPT_SCHEME, {{L"variation", activeVariation}});
|
return makePacket(AuthStage::ACCEPT_SCHEME, {{L"variation", activeVariation}});
|
||||||
}
|
}
|
||||||
|
|
||||||
case AuthStage::SCHEME_SETTINGS:
|
case AuthStage::SCHEME_SETTINGS:
|
||||||
{
|
{
|
||||||
|
if (!activeModule)
|
||||||
|
return fail();
|
||||||
wstring serverId = getField(packet->fields, L"serverId");
|
wstring serverId = getField(packet->fields, L"serverId");
|
||||||
wstring sessionEndpoint = getField(packet->fields, L"sessionEndpoint");
|
wstring joinUrlW = getField(packet->fields, L"joinUrl");
|
||||||
wstring scheme(activeModule->schemeName());
|
wstring scheme(activeModule->schemeName());
|
||||||
if (scheme == L"mcconsoles:session" && !accessToken.empty())
|
|
||||||
|
app.DebugPrintf("AUTH CLIENT: SCHEME_SETTINGS joinUrl=%ls serverId=%ls\n", joinUrlW.c_str(), serverId.c_str());
|
||||||
|
|
||||||
|
if (scheme == L"mcconsoles:session" && !accessToken.empty() && !joinUrlW.empty())
|
||||||
{
|
{
|
||||||
nlohmann::json body = {
|
nlohmann::json body = {
|
||||||
{"accessToken", narrowStr(accessToken)},
|
{"accessToken", narrowStr(accessToken)},
|
||||||
{"selectedProfile", narrowStr(clientUid)},
|
{"selectedProfile", narrowStr(clientUid)},
|
||||||
{"serverId", narrowStr(serverId)}
|
{"serverId", narrowStr(serverId)}
|
||||||
};
|
};
|
||||||
auto resp = HttpClient::post(narrowStr(sessionEndpoint) + "/session/minecraft/join", body.dump());
|
string joinUrl = narrowStr(joinUrlW);
|
||||||
if (resp.statusCode != 204)
|
app.DebugPrintf("AUTH CLIENT: POSTing join to %s\n", joinUrl.c_str());
|
||||||
|
HttpResponse resp;
|
||||||
|
try {
|
||||||
|
resp = HttpClient::post(joinUrl, body.dump());
|
||||||
|
} catch (...) {
|
||||||
|
app.DebugPrintf("AUTH CLIENT: join POST threw exception\n");
|
||||||
|
return fail();
|
||||||
|
}
|
||||||
|
app.DebugPrintf("AUTH CLIENT: join POST status=%d\n", resp.statusCode);
|
||||||
|
if (resp.statusCode < 200 || resp.statusCode >= 300)
|
||||||
return fail();
|
return fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ MapItem::MapItem(int id) : ComplexItem(id)
|
||||||
|
|
||||||
shared_ptr<MapItemSavedData> MapItem::getSavedData(short idNum, Level *level)
|
shared_ptr<MapItemSavedData> MapItem::getSavedData(short idNum, Level *level)
|
||||||
{
|
{
|
||||||
|
if (!level) return nullptr;
|
||||||
std::wstring id = wstring( L"map_" ) + std::to_wstring(idNum);
|
std::wstring id = wstring( L"map_" ) + std::to_wstring(idNum);
|
||||||
shared_ptr<MapItemSavedData> mapItemSavedData = dynamic_pointer_cast<MapItemSavedData>(level->getSavedData(typeid(MapItemSavedData), id));
|
shared_ptr<MapItemSavedData> mapItemSavedData = dynamic_pointer_cast<MapItemSavedData>(level->getSavedData(typeid(MapItemSavedData), id));
|
||||||
|
|
||||||
|
|
@ -42,6 +43,7 @@ shared_ptr<MapItemSavedData> MapItem::getSavedData(short idNum, Level *level)
|
||||||
|
|
||||||
shared_ptr<MapItemSavedData> MapItem::getSavedData(shared_ptr<ItemInstance> itemInstance, Level *level)
|
shared_ptr<MapItemSavedData> MapItem::getSavedData(shared_ptr<ItemInstance> itemInstance, Level *level)
|
||||||
{
|
{
|
||||||
|
if (!level) return nullptr;
|
||||||
MemSect(31);
|
MemSect(31);
|
||||||
std::wstring id = wstring( L"map_" ) + std::to_wstring(itemInstance->getAuxValue() );
|
std::wstring id = wstring( L"map_" ) + std::to_wstring(itemInstance->getAuxValue() );
|
||||||
MemSect(0);
|
MemSect(0);
|
||||||
|
|
@ -254,6 +256,7 @@ void MapItem::update(Level *level, shared_ptr<Entity> player, shared_ptr<MapItem
|
||||||
|
|
||||||
void MapItem::inventoryTick(shared_ptr<ItemInstance> itemInstance, Level *level, shared_ptr<Entity> owner, int slot, bool selected)
|
void MapItem::inventoryTick(shared_ptr<ItemInstance> itemInstance, Level *level, shared_ptr<Entity> owner, int slot, bool selected)
|
||||||
{
|
{
|
||||||
|
if (!level) return;
|
||||||
if (level->isClientSide) return;
|
if (level->isClientSide) return;
|
||||||
|
|
||||||
shared_ptr<MapItemSavedData> data = getSavedData(itemInstance, level);
|
shared_ptr<MapItemSavedData> data = getSavedData(itemInstance, level);
|
||||||
|
|
@ -293,7 +296,10 @@ void MapItem::inventoryTick(shared_ptr<ItemInstance> itemInstance, Level *level,
|
||||||
|
|
||||||
shared_ptr<Packet> MapItem::getUpdatePacket(shared_ptr<ItemInstance> itemInstance, Level *level, shared_ptr<Player> player)
|
shared_ptr<Packet> MapItem::getUpdatePacket(shared_ptr<ItemInstance> itemInstance, Level *level, shared_ptr<Player> player)
|
||||||
{
|
{
|
||||||
charArray data = MapItem::getSavedData(itemInstance, level)->getUpdatePacket(itemInstance, level, player);
|
if (!level) return nullptr;
|
||||||
|
auto savedData = MapItem::getSavedData(itemInstance, level);
|
||||||
|
if (!savedData) return nullptr;
|
||||||
|
charArray data = savedData->getUpdatePacket(itemInstance, level, player);
|
||||||
|
|
||||||
if (data.data == nullptr || data.length == 0) return nullptr;
|
if (data.data == nullptr || data.length == 0) return nullptr;
|
||||||
|
|
||||||
|
|
@ -304,6 +310,7 @@ shared_ptr<Packet> MapItem::getUpdatePacket(shared_ptr<ItemInstance> itemInstanc
|
||||||
|
|
||||||
void MapItem::onCraftedBy(shared_ptr<ItemInstance> itemInstance, Level *level, shared_ptr<Player> player)
|
void MapItem::onCraftedBy(shared_ptr<ItemInstance> itemInstance, Level *level, shared_ptr<Player> player)
|
||||||
{
|
{
|
||||||
|
if (!level) return;
|
||||||
wchar_t buf[64];
|
wchar_t buf[64];
|
||||||
|
|
||||||
int mapScale = 3;
|
int mapScale = 3;
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
#include "stdafx.h"
|
|
||||||
#include "OfflineAuthModule.h"
|
|
||||||
|
|
||||||
const wchar_t *KeypairOfflineAuthModule::schemeName() { return L"mcconsoles:keypair_offline"; }
|
|
||||||
|
|
||||||
vector<wstring> KeypairOfflineAuthModule::supportedVariations()
|
|
||||||
{
|
|
||||||
return {L"rsa2048", L"ed25519"};
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<pair<wstring, wstring>> KeypairOfflineAuthModule::getSettings(const wstring &variation)
|
|
||||||
{
|
|
||||||
return {{L"key_type", variation}};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KeypairOfflineAuthModule::onAuthData(const vector<pair<wstring, wstring>> &fields, wstring &outUid, wstring &outUsername)
|
|
||||||
{
|
|
||||||
return extractIdentity(fields, outUid, outUsername);
|
|
||||||
}
|
|
||||||
|
|
||||||
const wchar_t *OfflineAuthModule::schemeName() { return L"mcconsoles:offline"; }
|
|
||||||
|
|
||||||
vector<wstring> OfflineAuthModule::supportedVariations()
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<pair<wstring, wstring>> OfflineAuthModule::getSettings(const wstring &variation)
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OfflineAuthModule::onAuthData(const vector<pair<wstring, wstring>> &fields, wstring &outUid, wstring &outUsername)
|
|
||||||
{
|
|
||||||
return extractIdentity(fields, outUid, outUsername);
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
#pragma once
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#include "AuthModule.h"
|
|
||||||
|
|
||||||
class KeypairOfflineAuthModule : public AuthModule
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
const wchar_t *schemeName() override;
|
|
||||||
vector<wstring> supportedVariations() override;
|
|
||||||
vector<pair<wstring, wstring>> getSettings(const wstring &variation) override;
|
|
||||||
bool onAuthData(const vector<pair<wstring, wstring>> &fields, wstring &outUid, wstring &outUsername) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class OfflineAuthModule : public AuthModule
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
const wchar_t *schemeName() override;
|
|
||||||
vector<wstring> supportedVariations() override;
|
|
||||||
vector<pair<wstring, wstring>> getSettings(const wstring &variation) override;
|
|
||||||
bool onAuthData(const vector<pair<wstring, wstring>> &fields, wstring &outUid, wstring &outUsername) override;
|
|
||||||
};
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
#include "StringHelpers.h"
|
#include "StringHelpers.h"
|
||||||
#include "Common/vendor/nlohmann/json.hpp"
|
#include "Common/vendor/nlohmann/json.hpp"
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <fstream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
static wstring generateServerId()
|
static wstring generateServerId()
|
||||||
{
|
{
|
||||||
|
|
@ -14,50 +16,96 @@ static wstring generateServerId()
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionAuthModule::SessionAuthModule()
|
static vector<YggdrasilProviderConfig> s_registryProviders;
|
||||||
|
static bool s_registryLoaded = false;
|
||||||
|
|
||||||
|
void YggdrasilRegistry::load()
|
||||||
{
|
{
|
||||||
endpoints[L"mojang"] = {L"https://authserver.mojang.com", L"https://sessionserver.mojang.com"};
|
s_registryProviders.clear();
|
||||||
endpoints[L"elyby"] = {L"https://authserver.ely.by", L"https://authserver.ely.by"};
|
|
||||||
|
std::ifstream file("yggdrasil.json");
|
||||||
|
if (file.is_open())
|
||||||
|
{
|
||||||
|
auto j = nlohmann::json::parse(file, nullptr, false);
|
||||||
|
if (j.is_array())
|
||||||
|
{
|
||||||
|
for (const auto &entry : j)
|
||||||
|
{
|
||||||
|
if (!entry.is_object()) continue;
|
||||||
|
YggdrasilProviderConfig cfg;
|
||||||
|
cfg.name = convStringToWstring(entry.value("name", ""));
|
||||||
|
cfg.displayName = convStringToWstring(entry.value("displayName", ""));
|
||||||
|
cfg.authUrl = entry.value("authUrl", "");
|
||||||
|
cfg.sessionUrl = entry.value("sessionUrl", "");
|
||||||
|
if (!cfg.name.empty() && !cfg.sessionUrl.empty())
|
||||||
|
s_registryProviders.push_back(std::move(cfg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::none_of(s_registryProviders.begin(), s_registryProviders.end(),
|
||||||
|
[](const auto &p) { return p.name == L"elyby"; }))
|
||||||
|
s_registryProviders.insert(s_registryProviders.begin(),
|
||||||
|
{L"elyby", L"Ely.by", "https://authserver.ely.by/auth", "https://authserver.ely.by/session"});
|
||||||
|
|
||||||
|
s_registryProviders.push_back(
|
||||||
|
{L"mojang", L"Mojang", "", "https://sessionserver.mojang.com/session/minecraft"});
|
||||||
|
|
||||||
|
s_registryLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const vector<YggdrasilProviderConfig> &YggdrasilRegistry::providers()
|
||||||
|
{
|
||||||
|
if (!s_registryLoaded) load();
|
||||||
|
return s_registryProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
const YggdrasilProviderConfig *YggdrasilRegistry::find(const wstring &name)
|
||||||
|
{
|
||||||
|
for (const auto &p : providers())
|
||||||
|
if (p.name == name) return &p;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const YggdrasilProviderConfig &YggdrasilRegistry::defaultProvider()
|
||||||
|
{
|
||||||
|
const auto &list = providers();
|
||||||
|
for (const auto &p : list)
|
||||||
|
if (p.name != L"mojang") return p;
|
||||||
|
return list[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *SessionAuthModule::schemeName() { return L"mcconsoles:session"; }
|
const wchar_t *SessionAuthModule::schemeName() { return L"mcconsoles:session"; }
|
||||||
|
|
||||||
vector<wstring> SessionAuthModule::supportedVariations()
|
vector<wstring> SessionAuthModule::supportedVariations()
|
||||||
{
|
{
|
||||||
return {L"mojang", L"elyby"};
|
vector<wstring> result;
|
||||||
|
for (const auto &p : YggdrasilRegistry::providers())
|
||||||
|
result.push_back(p.name);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<pair<wstring, wstring>> SessionAuthModule::getSettings(const wstring &variation)
|
vector<pair<wstring, wstring>> SessionAuthModule::getSettings(const wstring &variation)
|
||||||
{
|
{
|
||||||
auto it = endpoints.find(variation);
|
auto *p = YggdrasilRegistry::find(variation);
|
||||||
if (it == endpoints.end()) return {};
|
if (!p) return {};
|
||||||
|
|
||||||
activeSessionEndpoint = it->second.sessionEndpoint;
|
activeProvider = p;
|
||||||
activeServerId = generateServerId();
|
activeServerId = generateServerId();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
{L"authEndpoint", it->second.authEndpoint},
|
{L"joinUrl", convStringToWstring(p->joinUrl())},
|
||||||
{L"sessionEndpoint", it->second.sessionEndpoint},
|
|
||||||
{L"serverId", activeServerId}
|
{L"serverId", activeServerId}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SessionAuthModule::onAuthData(const vector<pair<wstring, wstring>> &fields, wstring &outUid, wstring &outUsername)
|
bool SessionAuthModule::serverVerify(const YggdrasilProviderConfig &provider, const wstring &username, const wstring &serverId, wstring &outUid, wstring &outUsername)
|
||||||
{
|
{
|
||||||
wstring username;
|
string url = provider.hasJoinedUrl() + "?username=" + narrowStr(username) + "&serverId=" + narrowStr(serverId);
|
||||||
for (const auto &[k, v] : fields)
|
|
||||||
{
|
|
||||||
if (k == L"username") username = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (username.empty() || activeServerId.empty() || activeSessionEndpoint.empty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
string url = narrowStr(activeSessionEndpoint)
|
|
||||||
+ "/session/minecraft/hasJoined?username=" + narrowStr(username)
|
|
||||||
+ "&serverId=" + narrowStr(activeServerId);
|
|
||||||
|
|
||||||
|
app.DebugPrintf("AUTH SERVER [%ls]: hasJoined GET %s\n", provider.name.c_str(), url.c_str());
|
||||||
auto response = HttpClient::get(url);
|
auto response = HttpClient::get(url);
|
||||||
|
app.DebugPrintf("AUTH SERVER [%ls]: hasJoined status=%d\n", provider.name.c_str(), response.statusCode);
|
||||||
|
|
||||||
if (response.statusCode != 200)
|
if (response.statusCode != 200)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -67,12 +115,25 @@ bool SessionAuthModule::onAuthData(const vector<pair<wstring, wstring>> &fields,
|
||||||
|
|
||||||
string id = json.value("id", "");
|
string id = json.value("id", "");
|
||||||
string name = json.value("name", "");
|
string name = json.value("name", "");
|
||||||
|
|
||||||
if (id.empty() || name.empty())
|
if (id.empty() || name.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
outUid = convStringToWstring(id);
|
outUid = convStringToWstring(id);
|
||||||
outUsername = convStringToWstring(name);
|
outUsername = convStringToWstring(name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SessionAuthModule::onAuthData(const vector<pair<wstring, wstring>> &fields, wstring &outUid, wstring &outUsername)
|
||||||
|
{
|
||||||
|
wstring username;
|
||||||
|
for (const auto &[k, v] : fields)
|
||||||
|
if (k == L"username") username = v;
|
||||||
|
|
||||||
|
if (username.empty() || activeServerId.empty() || !activeProvider)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!serverVerify(*activeProvider, username, activeServerId, outUid, outUsername))
|
||||||
|
return false;
|
||||||
|
|
||||||
return validate(outUid, outUsername);
|
return validate(outUid, outUsername);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,41 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#include "AuthModule.h"
|
#include "AuthModule.h"
|
||||||
#include <unordered_map>
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
struct YggdrasilProviderConfig
|
||||||
|
{
|
||||||
|
wstring name;
|
||||||
|
wstring displayName;
|
||||||
|
string authUrl;
|
||||||
|
string sessionUrl;
|
||||||
|
string authenticateUrl() const { return authUrl + "/authenticate"; }
|
||||||
|
string validateUrl() const { return authUrl + "/validate"; }
|
||||||
|
string refreshUrl() const { return authUrl + "/refresh"; }
|
||||||
|
string joinUrl() const { return sessionUrl + "/join"; }
|
||||||
|
string hasJoinedUrl() const { return sessionUrl + "/hasJoined"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class YggdrasilRegistry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void load();
|
||||||
|
static const vector<YggdrasilProviderConfig> &providers();
|
||||||
|
static const YggdrasilProviderConfig *find(const wstring &name);
|
||||||
|
static const YggdrasilProviderConfig &defaultProvider();
|
||||||
|
};
|
||||||
|
|
||||||
class SessionAuthModule : public AuthModule
|
class SessionAuthModule : public AuthModule
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
struct EndpointPair {
|
|
||||||
wstring authEndpoint;
|
|
||||||
wstring sessionEndpoint;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unordered_map<wstring, EndpointPair> endpoints;
|
const YggdrasilProviderConfig *activeProvider = nullptr;
|
||||||
wstring activeSessionEndpoint;
|
|
||||||
wstring activeServerId;
|
wstring activeServerId;
|
||||||
|
|
||||||
|
bool serverVerify(const YggdrasilProviderConfig &provider, const wstring &username, const wstring &serverId, wstring &outUid, wstring &outUsername);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SessionAuthModule();
|
SessionAuthModule() = default;
|
||||||
|
|
||||||
const wchar_t *schemeName() override;
|
const wchar_t *schemeName() override;
|
||||||
vector<wstring> supportedVariations() override;
|
vector<wstring> supportedVariations() override;
|
||||||
|
|
|
||||||
|
|
@ -263,8 +263,6 @@ set(_MINECRAFT_WORLD_COMMON_NET_MINECRAFT_NETWORK_PACKET
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/AuthModule.h"
|
"${CMAKE_CURRENT_SOURCE_DIR}/AuthModule.h"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/SessionAuthModule.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/SessionAuthModule.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/SessionAuthModule.h"
|
"${CMAKE_CURRENT_SOURCE_DIR}/SessionAuthModule.h"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/OfflineAuthModule.cpp"
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/OfflineAuthModule.h"
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/AuthPackets.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/AuthPackets.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/AuthPackets.h"
|
"${CMAKE_CURRENT_SOURCE_DIR}/AuthPackets.h"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/UUID.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/UUID.cpp"
|
||||||
|
|
|
||||||
8
yggdrasil.json
Normal file
8
yggdrasil.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "elyby",
|
||||||
|
"displayName": "Ely.by",
|
||||||
|
"authUrl": "https://authserver.ely.by/auth",
|
||||||
|
"sessionUrl": "https://authserver.ely.by/session"
|
||||||
|
}
|
||||||
|
]
|
||||||
Loading…
Reference in a new issue