Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix: Discord authentication in consent mode (early WIP)
  • Loading branch information
blattersturm committed Mar 27, 2020
1 parent 29cd341 commit babb627
Showing 1 changed file with 136 additions and 133 deletions.
269 changes: 136 additions & 133 deletions code/components/discord/src/DiscordIdentityData.cpp
@@ -1,175 +1,178 @@
#include <StdInc.h>

#ifdef GTA_FIVE
#include <LegitimacyAPI.h>
#define CNL_ENDPOINT "https://lambda.fivem.net"

#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/client.hpp>
#include <LegitimacyAPI.h>

#include <json.hpp>

#include <skyr/url.hpp>

#include <HttpClient.h>

using client = websocketpp::client<websocketpp::config::asio_client>;
using message_ptr = websocketpp::config::asio_client::message_type::ptr;

constexpr auto MIN_PORT = 6463;
constexpr auto MAX_PORT = 6472;

using json = nlohmann::json;

static HookFunction initFunction([]()
{
std::thread([]()
{
try
{
client c;
c.init_asio();
HANDLE hPipe = INVALID_HANDLE_VALUE;

static int curPort;
for (int i = 0; i < 10; i++)
{
hPipe = CreateFileW(va(L"\\\\.\\pipe\\discord-ipc-%d", i), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

auto try_port = [&c](int port)
if (hPipe != INVALID_HANDLE_VALUE)
{
curPort = port;
break;
}
}

websocketpp::lib::error_code ec;
auto con = c.get_connection(fmt::sprintf("ws://127.0.0.1:%d/?v=1", port), ec);
if (hPipe == NULL || hPipe == INVALID_HANDLE_VALUE)
{
return;
}

if (!ec)
{
// this enables top secret internal RPC mode
con->append_header("origin", "https://discordapp.com");
c.connect(con);
}
};
auto writePipe = [hPipe](int opCode, const json& data)
{
auto dataStr = data.dump();

c.set_fail_handler([&try_port](websocketpp::connection_hdl hdl)
{
if (curPort == MAX_PORT)
{
return;
}
std::vector<uint8_t> dataBuffer(dataStr.length() + 4 + 4);
*(uint32_t*)&dataBuffer[0] = opCode;
*(uint32_t*)&dataBuffer[4] = dataStr.length();
memcpy(&dataBuffer[8], dataStr.data(), dataStr.length());

try_port(curPort + 1);
});
DWORD bytesWritten;
WriteFile(hPipe, dataBuffer.data(), dataBuffer.size(), &bytesWritten, NULL);
};

c.set_message_handler([&c](websocketpp::connection_hdl hdl, message_ptr msg)
{
auto obj = json::parse(msg->get_payload());
writePipe(0 /* HANDSHAKE */, json::object({
{ "v", 1},
{ "client_id", "382624125287399424"}
}));

auto cmd = obj.value("cmd", "");
if (cmd == "DISPATCH")
bool close = false;

auto closeConnection = [&writePipe, &close, hPipe]()
{
writePipe(2, {});
CloseHandle(hPipe);
};

auto handleMsg = [&writePipe, &closeConnection](int opCode, const json& data)
{
switch (opCode)
{
case 1: // FRAME
if (data["evt"] == "READY")
{
auto evt = obj.value("evt", "");
auto userId = data["data"]["user"]["id"].get<std::string>();

if (evt == "READY")
// check with CnL if we have access
Instance<::HttpClient>::Get()->DoPostRequest(
CNL_ENDPOINT "/api/validate/discord",
{
{ "entitlementId", ros::GetEntitlementSource() },
{ "userId", userId }
},
[writePipe, closeConnection](bool success, const char* data, size_t length)
{
// send overlay handshake
auto overlaySubscribe = json::object({
{"cmd", "SUBSCRIBE"},
{"args", json::object()},
{"evt", "OVERLAY"},
{"nonce", "nonce"}
});
if (!success && strstr(data, "HTTP 4") != nullptr)
{
writePipe(1 /* FRAME */, json::object({
{ "cmd", "AUTHORIZE" },
{ "args", json::object({
{ "scopes", json::array({"identify"}) },
{ "client_id", "382624125287399424" },
{ "redirect_url", "https://cfx.re" },
{ "prompt", "none" },
}) },
{ "nonce", "nonce1" },
}));
}
else
{
closeConnection();
}
});
}
else if (data["nonce"] == "nonce1")
{
if (data["evt"] == "ERROR")
{
// user probably denied auth request in Discord UI
// #TODO: store this and only ask again when asked by CfxUI
closeConnection();
}
else
{
auto code = data["data"]["code"].get<std::string>();

auto overlayInit = json::object({
{"cmd", "OVERLAY"},
{"args", json::object(
{
{"type", "CONNECT"},
{"pid", -1}
}
)},
{"nonce", "nonce2"}
Instance<::HttpClient>::Get()->DoPostRequest(
CNL_ENDPOINT "/api/validate/discord",
{
{ "entitlementId", ros::GetEntitlementSource() },
{ "authCode", code }
},
[](bool, const char*, size_t)
{

});

websocketpp::lib::error_code ec;
c.send(hdl, overlaySubscribe.dump(), websocketpp::frame::opcode::text, ec);
c.send(hdl, overlayInit.dump(), websocketpp::frame::opcode::text, ec);
closeConnection();
}
else if (evt == "OVERLAY")
{
auto& data = obj["data"];
}
break;
case 3: // PING
writePipe(4 /* PONG */, data);
break;
}
};

while (true)
{
if (close)
{
break;
}

// read from pipe
struct
{
uint32_t opCode;
uint32_t length;
} pktHdr;

DWORD bytesAvail = 0;

if (data["type"] == "DISPATCH")
if (PeekNamedPipe(hPipe, NULL, 0, NULL, &bytesAvail, NULL) && bytesAvail > 0)
{
DWORD numRead = 0;
ReadFile(hPipe, &pktHdr, sizeof(pktHdr), &numRead, NULL);

if (numRead == sizeof(pktHdr) && pktHdr.length > 0)
{
std::vector<uint8_t> buf(pktHdr.length);
ReadFile(hPipe, buf.data(), buf.size(), &numRead, NULL);

if (numRead == buf.size())
{
try
{
handleMsg(pktHdr.opCode, json::parse(buf));
}
catch (json::exception& e)
{
auto& payloads = data["payloads"];

for (auto& payload : payloads)
{
if (payload["type"] == "OVERLAY_INITIALIZE")
{
auto token = payload["token"].get<std::string>();

auto authRequest = json::object({
{ "permissions", 0 },
{ "authorize", true }
});

c.close(hdl, 0, "OK");

HttpRequestOptions options;
options.headers.insert({ "Content-Type", "application/json" });
options.headers.insert({ "Authorization", token });

Instance<HttpClient>::Get()->DoPostRequest(
"https://discordapp.com/api/v6/oauth2/authorize?client_id=530784411080458270&response_type=code&scope=identify",
authRequest.dump(),
options,
[](bool result, const char* data, size_t len)
{
if (result)
{
try
{
auto obj = json::parse(std::string(data, len));
auto location = obj.value("location", "");

if (!location.empty())
{
auto uri = skyr::make_url(location);

if (uri)
{
auto queryStr = uri->search();
queryStr = queryStr.substr(queryStr.find_first_of("=") + 1);

Instance<::HttpClient>::Get()->DoPostRequest(
"https://lambda.fivem.net/api/validate/discord",
{
{ "entitlementId", ros::GetEntitlementSource() },
{ "authCode", queryStr }
},
[](bool, const char*, size_t)
{

});
}
}
}
catch (std::exception& e)
{

}
}
});
}
}
}
}
}
});

try_port(MIN_PORT);

c.run();
}
catch (websocketpp::exception const& e)
{
trace("discord auth failed: %s\n", e.what());
}
else
{
Sleep(50);
}
}
}).detach();
});
Expand Down

0 comments on commit babb627

Please sign in to comment.