mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-08-20 09:57:09 +00:00
finally added session-based authentication and libcurl for https requests. (serverside for right now.)
This commit is contained in:
parent
e979be42b2
commit
0fa5ae3037
|
|
@ -4,6 +4,21 @@ project(MinecraftConsoles LANGUAGES C CXX RC ASM_MASM)
|
|||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
include(FetchContent)
|
||||
set(BUILD_CURL_EXE OFF CACHE BOOL "" FORCE)
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||
set(CURL_USE_SCHANNEL ON CACHE BOOL "" FORCE)
|
||||
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
|
||||
set(CURL_DISABLE_LDAP ON CACHE BOOL "" FORCE)
|
||||
set(CURL_DISABLE_LDAPS ON CACHE BOOL "" FORCE)
|
||||
FetchContent_Declare(
|
||||
curl
|
||||
URL https://github.com/curl/curl/releases/download/curl-8_11_1/curl-8.11.1.tar.xz
|
||||
URL_HASH SHA256=c7ca7db48b0909743eaef34250da02c19bc61d4f1dcedd6603f109409536ab56
|
||||
FIND_PACKAGE_ARGS
|
||||
)
|
||||
FetchContent_MakeAvailable(curl)
|
||||
|
||||
if(NOT WIN32)
|
||||
message(FATAL_ERROR "This CMake build currently supports Windows only.")
|
||||
|
|
@ -18,7 +33,6 @@ set(CMAKE_CONFIGURATION_TYPES
|
|||
"Release"
|
||||
CACHE STRING "" FORCE
|
||||
)
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
|
||||
function(configure_compiler_target target)
|
||||
# MSVC and compatible compilers (like Clang-cl)
|
||||
|
|
|
|||
|
|
@ -4139,8 +4139,7 @@ ClientConnection::DeferredEntityLinkPacket::DeferredEntityLinkPacket(shared_ptr<
|
|||
void ClientConnection::beginAuth()
|
||||
{
|
||||
handshakeManager = new HandshakeManager(false);
|
||||
handshakeManager->registerModule(new MojangAuthModule());
|
||||
handshakeManager->registerModule(new ElyByAuthModule());
|
||||
handshakeManager->registerModule(new SessionAuthModule());
|
||||
handshakeManager->registerModule(new KeypairOfflineAuthModule());
|
||||
handshakeManager->registerModule(new OfflineAuthModule());
|
||||
|
||||
|
|
|
|||
|
|
@ -411,8 +411,7 @@ bool PendingConnection::isDisconnected()
|
|||
void PendingConnection::initAuth()
|
||||
{
|
||||
handshakeManager = new HandshakeManager(true);
|
||||
handshakeManager->registerModule(new MojangAuthModule());
|
||||
handshakeManager->registerModule(new ElyByAuthModule());
|
||||
handshakeManager->registerModule(new SessionAuthModule());
|
||||
handshakeManager->registerModule(new KeypairOfflineAuthModule());
|
||||
handshakeManager->registerModule(new OfflineAuthModule());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,23 @@
|
|||
#include "stdafx.h"
|
||||
#include "AuthModule.h"
|
||||
#include "HttpClient.h"
|
||||
#include "StringHelpers.h"
|
||||
#include "Common/vendor/nlohmann/json.hpp"
|
||||
#include <random>
|
||||
|
||||
static string narrowStr(const wstring &w)
|
||||
{
|
||||
return string(w.begin(), w.end());
|
||||
}
|
||||
|
||||
static wstring generateServerId()
|
||||
{
|
||||
static constexpr wchar_t hex[] = L"0123456789abcdef";
|
||||
static std::mt19937 rng(std::random_device{}());
|
||||
wstring id(16, L'0');
|
||||
for (auto &c : id) c = hex[rng() & 0xF];
|
||||
return id;
|
||||
}
|
||||
|
||||
bool AuthModule::validate(const wstring &uid, const wstring &username)
|
||||
{
|
||||
|
|
@ -16,38 +34,67 @@ bool AuthModule::extractIdentity(const vector<pair<wstring, wstring>> &fields, w
|
|||
return validate(outUid, outUsername);
|
||||
}
|
||||
|
||||
ElyByAuthModule::ElyByAuthModule(const wstring &endpoint)
|
||||
: endpoint(endpoint)
|
||||
SessionAuthModule::SessionAuthModule()
|
||||
{
|
||||
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"};
|
||||
}
|
||||
|
||||
const wchar_t *ElyByAuthModule::schemeName() { return L"mcconsoles:elyby"; }
|
||||
const wchar_t *SessionAuthModule::schemeName() { return L"mcconsoles:session"; }
|
||||
|
||||
vector<wstring> ElyByAuthModule::supportedVariations()
|
||||
vector<wstring> SessionAuthModule::supportedVariations()
|
||||
{
|
||||
return {L"java"};
|
||||
return {L"mojang", L"elyby"};
|
||||
}
|
||||
|
||||
vector<pair<wstring, wstring>> ElyByAuthModule::getSettings(const wstring &variation)
|
||||
vector<pair<wstring, wstring>> SessionAuthModule::getSettings(const wstring &variation)
|
||||
{
|
||||
return {{L"endpoint", endpoint}};
|
||||
auto it = endpoints.find(variation);
|
||||
if (it == endpoints.end()) return {};
|
||||
|
||||
activeSessionEndpoint = it->second.sessionEndpoint;
|
||||
activeServerId = generateServerId();
|
||||
|
||||
return {
|
||||
{L"authEndpoint", it->second.authEndpoint},
|
||||
{L"sessionEndpoint", it->second.sessionEndpoint},
|
||||
{L"serverId", activeServerId}
|
||||
};
|
||||
}
|
||||
|
||||
bool ElyByAuthModule::onAuthData(const vector<pair<wstring, wstring>> &fields, wstring &outUid, wstring &outUsername)
|
||||
bool SessionAuthModule::onAuthData(const vector<pair<wstring, wstring>> &fields, wstring &outUid, wstring &outUsername)
|
||||
{
|
||||
return extractIdentity(fields, outUid, outUsername);
|
||||
}
|
||||
wstring username;
|
||||
for (const auto &[k, v] : fields)
|
||||
{
|
||||
if (k == L"username") username = v;
|
||||
}
|
||||
|
||||
MojangAuthModule::MojangAuthModule()
|
||||
: ElyByAuthModule(L"https://sessionserver.mojang.com")
|
||||
{
|
||||
}
|
||||
if (username.empty() || activeServerId.empty() || activeSessionEndpoint.empty())
|
||||
return false;
|
||||
|
||||
const wchar_t *MojangAuthModule::schemeName() { return L"mcconsoles:mojang"; }
|
||||
string url = narrowStr(activeSessionEndpoint)
|
||||
+ "/session/minecraft/hasJoined?username=" + narrowStr(username)
|
||||
+ "&serverId=" + narrowStr(activeServerId);
|
||||
|
||||
vector<wstring> MojangAuthModule::supportedVariations()
|
||||
{
|
||||
return {L"java", L"bedrock"};
|
||||
auto response = HttpClient::get(url);
|
||||
if (response.statusCode != 200)
|
||||
return false;
|
||||
|
||||
auto json = nlohmann::json::parse(response.body, nullptr, false);
|
||||
if (json.is_discarded())
|
||||
return false;
|
||||
|
||||
string id = json.value("id", "");
|
||||
string name = json.value("name", "");
|
||||
|
||||
if (id.empty() || name.empty())
|
||||
return false;
|
||||
|
||||
outUid = convStringToWstring(id);
|
||||
outUsername = convStringToWstring(name);
|
||||
|
||||
return validate(outUid, outUsername);
|
||||
}
|
||||
|
||||
const wchar_t *KeypairOfflineAuthModule::schemeName() { return L"mcconsoles:keypair_offline"; }
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using namespace std;
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <unordered_map>
|
||||
|
||||
class AuthModule
|
||||
{
|
||||
|
|
@ -21,13 +22,21 @@ protected:
|
|||
bool extractIdentity(const vector<pair<wstring, wstring>> &fields, wstring &outUid, wstring &outUsername);
|
||||
};
|
||||
|
||||
class ElyByAuthModule : public AuthModule
|
||||
class SessionAuthModule : public AuthModule
|
||||
{
|
||||
protected:
|
||||
wstring endpoint;
|
||||
public:
|
||||
struct EndpointPair {
|
||||
wstring authEndpoint;
|
||||
wstring sessionEndpoint;
|
||||
};
|
||||
|
||||
private:
|
||||
unordered_map<wstring, EndpointPair> endpoints;
|
||||
wstring activeSessionEndpoint;
|
||||
wstring activeServerId;
|
||||
|
||||
public:
|
||||
ElyByAuthModule(const wstring &endpoint = L"https://authserver.ely.by");
|
||||
SessionAuthModule();
|
||||
|
||||
const wchar_t *schemeName() override;
|
||||
vector<wstring> supportedVariations() override;
|
||||
|
|
@ -35,15 +44,6 @@ public:
|
|||
bool onAuthData(const vector<pair<wstring, wstring>> &fields, wstring &outUid, wstring &outUsername) override;
|
||||
};
|
||||
|
||||
class MojangAuthModule : public ElyByAuthModule
|
||||
{
|
||||
public:
|
||||
MojangAuthModule();
|
||||
|
||||
const wchar_t *schemeName() override;
|
||||
vector<wstring> supportedVariations() override;
|
||||
};
|
||||
|
||||
class KeypairOfflineAuthModule : public AuthModule
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -27,4 +27,6 @@ target_compile_definitions(Minecraft.World PRIVATE
|
|||
)
|
||||
target_precompile_headers(Minecraft.World PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:stdafx.h>")
|
||||
|
||||
target_link_libraries(Minecraft.World PUBLIC CURL::libcurl)
|
||||
|
||||
configure_compiler_target(Minecraft.World)
|
||||
|
|
|
|||
|
|
@ -79,7 +79,8 @@ shared_ptr<AuthPacket> HandshakeManager::handleServer(const shared_ptr<AuthPacke
|
|||
wstring uid, username;
|
||||
if (!activeModule->onAuthData(packet->fields, uid, username))
|
||||
return fail();
|
||||
|
||||
finalUid = uid;
|
||||
finalUsername = username;
|
||||
state = HandshakeState::AUTH_DATA_EXCHANGED;
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -93,11 +94,9 @@ shared_ptr<AuthPacket> HandshakeManager::handleServer(const shared_ptr<AuthPacke
|
|||
else if (k == L"username") username = v;
|
||||
}
|
||||
|
||||
if (!activeModule->validate(uid, username))
|
||||
if (uid != finalUid || username != finalUsername)
|
||||
return fail();
|
||||
|
||||
finalUid = uid;
|
||||
finalUsername = username;
|
||||
state = HandshakeState::IDENTITY_ASSIGNED;
|
||||
return makePacket(AuthStage::ASSIGN_IDENTITY, {
|
||||
{L"uid", finalUid},
|
||||
|
|
|
|||
57
Minecraft.World/HttpClient.cpp
Normal file
57
Minecraft.World/HttpClient.cpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#include "stdafx.h"
|
||||
#include "HttpClient.h"
|
||||
#include <curl/curl.h>
|
||||
|
||||
static size_t writeCallback(char *data, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
auto *buf = static_cast<std::string *>(userdata);
|
||||
buf->append(data, size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
static HttpResponse performRequest(CURL *curl)
|
||||
{
|
||||
std::string responseBody;
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseBody);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
|
||||
int statusCode = 0;
|
||||
if (res == CURLE_OK)
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode);
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
return {statusCode, std::move(responseBody)};
|
||||
}
|
||||
|
||||
HttpResponse HttpClient::get(const std::string &url)
|
||||
{
|
||||
CURL *curl = curl_easy_init();
|
||||
if (!curl)
|
||||
return {0, ""};
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
return performRequest(curl);
|
||||
}
|
||||
|
||||
HttpResponse HttpClient::post(const std::string &url, const std::string &body, const std::string &contentType)
|
||||
{
|
||||
CURL *curl = curl_easy_init();
|
||||
if (!curl)
|
||||
return {0, ""};
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast<long>(body.size()));
|
||||
|
||||
struct curl_slist *headers = nullptr;
|
||||
headers = curl_slist_append(headers, ("Content-Type: " + contentType).c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
HttpResponse resp = performRequest(curl);
|
||||
curl_slist_free_all(headers);
|
||||
return resp;
|
||||
}
|
||||
16
Minecraft.World/HttpClient.h
Normal file
16
Minecraft.World/HttpClient.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
struct HttpResponse
|
||||
{
|
||||
int statusCode;
|
||||
std::string body;
|
||||
};
|
||||
|
||||
class HttpClient
|
||||
{
|
||||
public:
|
||||
static HttpResponse get(const std::string &url);
|
||||
static HttpResponse post(const std::string &url, const std::string &body, const std::string &contentType = "application/json");
|
||||
};
|
||||
|
|
@ -320,6 +320,8 @@ set(_MINECRAFT_WORLD_COMMON_NET_MINECRAFT_NETWORK_PACKET
|
|||
"${CMAKE_CURRENT_SOURCE_DIR}/GetInfoPacket.h"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/HandshakeManager.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/HandshakeManager.h"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/HttpClient.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/HttpClient.h"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/InteractPacket.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/InteractPacket.h"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/KeepAlivePacket.cpp"
|
||||
|
|
|
|||
Loading…
Reference in a new issue