major refactor: auth v3

This commit is contained in:
Matthew Toro 2026-04-06 02:33:39 -04:00
parent 8a8b4b2573
commit 179d21d7b6
18 changed files with 376 additions and 166 deletions

View file

@ -4,6 +4,8 @@
#include "StringHelpers.h"
#include "Common/vendor/nlohmann/json.hpp"
#include <random>
#include <fstream>
#include <algorithm>
static wstring generateServerId()
{
@ -14,50 +16,96 @@ static wstring generateServerId()
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"};
endpoints[L"elyby"] = {L"https://authserver.ely.by", L"https://authserver.ely.by"};
s_registryProviders.clear();
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"; }
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)
{
auto it = endpoints.find(variation);
if (it == endpoints.end()) return {};
auto *p = YggdrasilRegistry::find(variation);
if (!p) return {};
activeSessionEndpoint = it->second.sessionEndpoint;
activeProvider = p;
activeServerId = generateServerId();
return {
{L"authEndpoint", it->second.authEndpoint},
{L"sessionEndpoint", it->second.sessionEndpoint},
{L"joinUrl", convStringToWstring(p->joinUrl())},
{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;
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);
string url = provider.hasJoinedUrl() + "?username=" + narrowStr(username) + "&serverId=" + narrowStr(serverId);
app.DebugPrintf("AUTH SERVER [%ls]: hasJoined GET %s\n", provider.name.c_str(), url.c_str());
auto response = HttpClient::get(url);
app.DebugPrintf("AUTH SERVER [%ls]: hasJoined status=%d\n", provider.name.c_str(), response.statusCode);
if (response.statusCode != 200)
return false;
@ -67,12 +115,25 @@ bool SessionAuthModule::onAuthData(const vector<pair<wstring, wstring>> &fields,
string id = json.value("id", "");
string name = json.value("name", "");
if (id.empty() || name.empty())
return false;
outUid = convStringToWstring(id);
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);
}