1
Fork 0
mirror of https://github.com/redstrate/Astra.git synced 2025-05-17 14:47:45 +00:00

Grab steam ticket from steamwrap, give it to the oauth top API

This puts everything in place now, and yet Square Enix refuses to
my session still :-(
This commit is contained in:
Joshua Goins 2025-05-04 16:33:07 -04:00
parent 3d39887d9c
commit 05d33dc5d7
8 changed files with 39 additions and 43 deletions

View file

@ -15,8 +15,6 @@ plugins!
* Game Patching Support: Can patch the game without the need to boot into the official launcher. * Game Patching Support: Can patch the game without the need to boot into the official launcher.
* Alternative Server Support: Can use alternative servers in case the official ones ever disappear. * Alternative Server Support: Can use alternative servers in case the official ones ever disappear.
**Note:** Steam-linked Square Enix accounts are not currently supported. You will have to use the official launcher or XIVLauncher.Core.
## Get It ## Get It
Details on where to find stable releases of Astra can be found on its [homepage](https://xiv.zone/astra/install). Details on where to find stable releases of Astra can be found on its [homepage](https://xiv.zone/astra/install).

View file

@ -12,30 +12,37 @@ class EncryptedArgTest : public QObject
private Q_SLOTS: private Q_SLOTS:
void steamTicket_data() void steamTicket_data()
{ {
QTest::addColumn<QByteArray>("ticketBytes"); QTest::addColumn<QString>("ticketBytes");
QTest::addColumn<uint32_t>("time"); QTest::addColumn<uint32_t>("time");
QTest::addColumn<QString>("encryptedTicket"); QTest::addColumn<QString>("encryptedTicket");
QTest::addColumn<int>("encryptedLength");
QTest::addRow("test 1") << QByteArrayLiteral( QTest::addRow("real ticket")
"caf59103e80d600b4a9233358c131a437cedcf8802eb705223cce84278e6cb4c3655a7accd98097c06748b33d340f9de79920754616e295b88b833d9d84412ffc6a4406c60a6691ce5" << QStringLiteral(
"2c27b5b1c90d36a6a810cef4c81fdc3c75aaa87353433f2f3c7232c00d1198a79bb27df6edb89c5fd0a3a11e957c40") "140000009efb2c79d200f07599f633050100100195c517681800000001000000020000002c9ea991afec1a49f7e72d000b000000b8000000380000000400000099f63305010"
<< static_cast<uint32_t>(1746386404) "010012a990000c47323496501a8c000000000807a1268002a2e680100908400000100c5000400000000004e408e518c259b8b329b5fcfdb00903fadad36d7e088813dc9b174"
<< QStringLiteral( "b06e08b69f8f46d083cf4118d1eb4062e009147906d9c80759af3eb221f7fbb8e28d402e11ba80e3cf4a101487f87ccce9688daf99dd412e6bcaab6e58f70030ff99323e4c8"
"jX6TeGNIJFgecYIQU4YLSnhkbCpTihoIBB6Dw5iw7rAWQiULVB-NhOsfJwLh7EHW5wYwU1XRJAy5dw6JCWQ6v0R5SwP_84DIVDT5gfET_" "1824659f7aa89188fadc6402bcb540843e1578cd112d4536fd65c4bd926f754")
"BoXmUr7rfXtJF62vpZ16XfwH2-P2KlaVRTaSYQ8xqTqC_fef6agOdYHvL_g-cNyjjv3xTMsekWnDCDhdG3Y1NvnPcXdAX9v0pjHUu5W5-" << static_cast<uint32_t>(1746389891)
"k8iZhUeTcTjYhMODBPNXz6uf7mKd1wkwAsTnDZoL89k8U1asq78hyaiWsFb4Nb39vK0n0TZfb20yFoXZh9NRO2VpgS,dSTy86oaqm2ZjKu7FnnHmFU6R_" << QStringLiteral(
"lS8pk8EB6itIekPAfC37LYSBIEI1rT9cEvoXNkX6hIGnZ5sthiNPoi5nx9_HdjWYQ9R0Kar-Bgjeu77Av753T0GpVhJhYFyBkMe-" "Tjr8Lv1O0HjJ7U4dOfkA9BdLAnEaCl_TU0GnYGGLTBM06TV9Ggf-fYb7WMqD-Xv758Q1zzSPTeaJctl8au-"
"NAqGMEjkYFjRUwOX9pEJGEszEL3_mWEbryQ8wL4ZaYk5xu4HAXe5hRwk-JUv__BW8IpiR_OsphQUgeKtRmXUPw1eIU2NYdsd3AJLAP3tiiENaplJ_y8X_" "imM4ACRgl0Y4LqJpLFfgBhkumd4dne2P9oM6qLzMnHfspPq8AFQFHXaiSicu2gSaCwpk36ZK-WX17DaTOkYFncIKl_rSZAkb8OzTpNX0aB_590hUpAf74-"
"OmC8tzvKCCdXapsas3-Won2R_ryQVJlB9j1tAARpNanIEwhOf9CHmbsYM,-8tTNfrKABIfOSlCdr2ajhLqF1i4hRiM-jSzISXAEmc-Nj75Rrg*"); "TU368A1fgXLw2aunwn0wBNvz0ywFEiAjmD8PfgUzA6IrvkP1eoKoY4A_NNBXnirca7CjWxOoguXRGaHjzq9vrDm8ABTk2o0u29R,Nqmz_4LN1Fj9cNhtyhHTXuV6huLxmsflb_"
"6DR5B8dwk8IMYup0z5AXHhLww0BmZkDKKCWjVehxWvoHkz8FNViV9Oduwv7ZGyHUYs47HUpOIr1Wirp6LEvsxBcDBf-T_XOK945j-z_"
"MtxXiNKqtAuaL8iw7OOIpVnXqIa77yGuOFFW-u2wv1cK1M3s_OqmgEdj0JZfoYbjT6lIEVsSXKMYwwf9zkAjx23K-gqrM8c8nStv4EYT7ZU6o_"
"I0KZ6OJVnCFElYLamz82NIRiPdzyuJcPoslNCXpQV_vWlyGJ0OIoR,2MrkkwMnTNx7HR4FJ6ACh0cQZmdBEB2pM4eQSqpJEC367JtCMzM*")
<< 652;
} }
void steamTicket() void steamTicket()
{ {
QFETCH(QByteArray, ticketBytes); QFETCH(QString, ticketBytes);
QFETCH(uint32_t, time); QFETCH(uint32_t, time);
QFETCH(QString, encryptedTicket); QFETCH(QString, encryptedTicket);
QFETCH(int, encryptedLength);
QCOMPARE(encryptSteamTicket(ticketBytes, time), encryptedTicket); auto pair = std::pair{encryptedTicket, encryptedLength};
QCOMPARE(encryptSteamTicket(ticketBytes, time), pair);
} }
}; };

View file

@ -6,4 +6,4 @@
#include <QString> #include <QString>
QString encryptGameArg(const QString &arg); QString encryptGameArg(const QString &arg);
QString encryptSteamTicket(const QByteArray &ticket, uint32_t time); std::pair<QString, int> encryptSteamTicket(QString ticket, uint32_t time);

View file

@ -16,7 +16,7 @@ public:
QCoro::Task<> initialize(); QCoro::Task<> initialize();
QCoro::Task<> shutdown(); QCoro::Task<> shutdown();
QCoro::Task<QString> getTicket(); QCoro::Task<std::pair<QString, int>> getTicket();
private: private:
QNetworkAccessManager m_qnam; QNetworkAccessManager m_qnam;

View file

@ -101,13 +101,13 @@ QStringList intoChunks(const QString &str, const int maxChunkSize)
return chunks; return chunks;
} }
QString encryptSteamTicket(const QByteArray &ticket, uint32_t time) std::pair<QString, int> encryptSteamTicket(QString ticket, uint32_t time)
{ {
// Round the time down // Round the time down
time -= 5; time -= 5;
time -= time % 60; time -= time % 60;
auto ticketString = QString::fromLatin1(ticket.toHex()).remove(QLatin1Char('-')).toLower(); auto ticketString = ticket.remove(QLatin1Char('-')).toLower();
auto rawTicketBytes = ticketString.toLatin1(); auto rawTicketBytes = ticketString.toLatin1();
rawTicketBytes.append('\0'); rawTicketBytes.append('\0');
@ -160,5 +160,8 @@ QString encryptSteamTicket(const QByteArray &ticket, uint32_t time)
encoded.replace('/', '_'); encoded.replace('/', '_');
encoded.replace('=', '*'); encoded.replace('=', '*');
return intoChunks(QString::fromLatin1(encoded), SPLIT_SIZE).join(QLatin1Char(',')); const auto parts = intoChunks(QString::fromLatin1(encoded), SPLIT_SIZE);
const auto finalString = parts.join(QLatin1Char(','));
return {finalString, finalString.length() - (parts.length() - 1)};
} }

View file

@ -232,12 +232,13 @@ QCoro::Task<std::optional<SquareEnixLogin::StoredInfo>> SquareEnixLogin::getStor
if (m_info->profile->account()->config()->license() == Account::GameLicense::WindowsSteam) { if (m_info->profile->account()->config()->license() == Account::GameLicense::WindowsSteam) {
query.addQueryItem(QStringLiteral("issteam"), QString::number(1)); query.addQueryItem(QStringLiteral("issteam"), QString::number(1));
// initialize the steam api
co_await m_launcher.steamApi()->initialize(); co_await m_launcher.steamApi()->initialize();
auto ticket = co_await m_launcher.steamApi()->getTicket(); // grab an auth ticket
auto [ticket, ticketSize] = co_await m_launcher.steamApi()->getTicket();
query.addQueryItem(QStringLiteral("session_ticket"), ticket); query.addQueryItem(QStringLiteral("session_ticket"), ticket);
query.addQueryItem(QStringLiteral("ticket_size"), QString::number(ticket.length())); query.addQueryItem(QStringLiteral("ticket_size"), QString::number(ticketSize));
} }
QUrl url; QUrl url;

View file

@ -34,7 +34,7 @@ QCoro::Task<> SteamAPI::shutdown()
Q_UNUSED(co_await m_qnam.post(QNetworkRequest(url), QByteArray{})) Q_UNUSED(co_await m_qnam.post(QNetworkRequest(url), QByteArray{}))
} }
QCoro::Task<QString> SteamAPI::getTicket() QCoro::Task<std::pair<QString, int>> SteamAPI::getTicket()
{ {
QUrl url; QUrl url;
url.setScheme(QStringLiteral("http")); url.setScheme(QStringLiteral("http"));
@ -45,5 +45,7 @@ QCoro::Task<QString> SteamAPI::getTicket()
const auto reply = co_await m_qnam.get(QNetworkRequest(url)); const auto reply = co_await m_qnam.get(QNetworkRequest(url));
const auto ticketBytes = reply->readAll(); const auto ticketBytes = reply->readAll();
co_return encryptSteamTicket(ticketBytes, 5); // TOOD: get time const QJsonDocument document = QJsonDocument::fromJson(ticketBytes);
co_return encryptSteamTicket(document[QStringLiteral("ticket")].toString(), document[QStringLiteral("ticket")].toInt());
} }

View file

@ -47,13 +47,6 @@ FormCard.FormCardPage {
description: i18n("If the account holds multiple licenses, choose the preferred one.") description: i18n("If the account holds multiple licenses, choose the preferred one.")
model: ["Windows", "Steam", "macOS"] model: ["Windows", "Steam", "macOS"]
text: i18n("License") text: i18n("License")
onCurrentIndexChanged: {
if (currentIndex === 1) {
currentIndex = 0;
errorDialog.open();
}
}
} }
FormCard.FormDelegateSeparator { FormCard.FormDelegateSeparator {
above: licenseField above: licenseField
@ -87,12 +80,4 @@ FormCard.FormCardPage {
} }
} }
} }
Kirigami.PromptDialog {
id: errorDialog
showCloseButton: false
standardButtons: Kirigami.Dialog.Ok
title: i18n("Steam Warning")
subtitle: i18n("Steam linked Square Enix accounts are not currently supported. You will have to use another launcher that supports these, such as the official launcher or XIVLauncher.Core.")
}
} }