11// SPDX-License-Identifier: GPL-3.0-only
22/*
33 * Freesm Launcher - Minecraft Launcher
4- * Copyright (C) 2025 so5iso4ka <so5iso4ka@icloud.com>
4+ * Copyright (C) 2026 so5iso4ka <so5iso4ka@icloud.com>
55 *
66 * This program is free software: you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
1818
1919#include " CustomAuthStep.h"
2020
21+ #include < QDateTime>
2122#include < QInputDialog>
23+ #include < QJsonDocument>
24+ #include < utility>
2225
2326#include " Application.h"
2427#include " Logging.h"
2528#include " net/NetUtils.h"
2629#include " net/RawHeaderProxy.h"
2730
28- # include < utility >
31+ CustomAuthStep::CustomAuthStep (AccountData* data, QString password) : AuthStep(data), m_password(std::move(password)) {}
2932
30- CustomAuthStep::CustomAuthStep (AccountData* data, AuthFlow::Action action, QString password)
31- : AuthStep(data), m_password(std::move(password)), m_action(action)
32- {}
33+ CustomAuthStep::~CustomAuthStep () = default ;
3334
3435void CustomAuthStep::perform ()
3536{
36- const QUrl url (authUrl () + requestUrl ());
37- const QString requestData = fillRequest ();
37+ if (m_data == nullptr ) {
38+ emit finished (AccountTaskState::STATE_FAILED_SOFT, tr (" Account data is a null pointer" ));
39+ return ;
40+ }
41+
42+ const QUrl url (m_data->authUrl + m_data->loginUrl );
43+ const QJsonDocument request (fillRequest ());
3844
39- m_response. reset ( new QByteArray () );
40- m_request = Net::Upload::makeByteArray (url, m_response, requestData. toUtf8 ());
45+ m_response = std::make_shared< QByteArray>( );
46+ m_request = Net::Upload::makeByteArray (url, m_response, request. toJson ());
4147
42- const auto headerProxy =
43- new Net::RawHeaderProxy (QList<Net::HeaderPair>{ { " Content-Type" , " application/json" }, { " Accept" , " application/json" } });
44- m_request->addHeaderProxy (headerProxy);
4548 // RawHeaderProxy::addHeaderProxy takes ownership of the proxy, so no cleanup is required
49+ m_request->addHeaderProxy (new Net::RawHeaderProxy (
50+ QList<Net::HeaderPair>{ { " Content-Type" , " application/json; charset=utf-8" }, { " Accept" , " application/json" } }));
4651
47- m_task.reset (new NetJob (authType () + " AuthStep " , APPLICATION->network ()));
52+ m_task.reset (new NetJob (" CustomAuthStep " , APPLICATION->network ()));
4853 m_task->setAskRetry (false );
4954 m_task->addNetAction (m_request);
5055
5156 connect (m_task.get (), &Task::finished, this , &CustomAuthStep::onRequestDone);
5257
5358 m_task->start ();
54- qDebug () << " Getting authorization token for " + authType () + " account" ;
59+ qDebug () << " Getting authorization token for custom account" ;
5560}
5661
57- QString CustomAuthStep::requestUrl ()
62+ QJsonObject CustomAuthStep::fillRequest () const
5863{
59- return m_action == AuthFlow::Action::Login ? m_data->loginUrl : m_data->refreshUrl ;
60- }
64+ QJsonObject root;
65+ root.insert (" username" , m_data->accountLogin );
66+ root.insert (" password" , m_password);
6167
62- QString CustomAuthStep::requestTemplate ()
63- {
64- if (m_action == AuthFlow::Action::Login) {
65- return R"XXX(
66- {
67- "username": "%1",
68- "password": "%2",
69- "clientToken": "%3",
70- "requestUser": false,
71- "agent": {
72- "name":"Minecraft",
73- "version":1
74- }
75- }
76- )XXX" ;
77- } else {
78- return R"XXX(
79- {
80- "accessToken": "%1",
81- "clientToken": "%2",
82- "requestUser": false,
83- "selectedProfile": {
84- "id": "%3",
85- "name": "%4"
86- }
87- }
88- )XXX" ;
89- }
90- }
68+ QJsonObject agent;
69+ agent.insert (" name" , " Minecraft" );
70+ agent.insert (" version" , 1 );
9171
92- QString CustomAuthStep::fillRequest ()
93- {
94- if (m_action == AuthFlow::Action::Login) {
95- return requestTemplate ().arg (m_data->accountLogin , m_password, clientID ());
96- } else {
97- return requestTemplate ().arg (m_data->yggdrasilToken .token , m_data->clientID , m_data->minecraftProfile .id ,
98- m_data->minecraftProfile .name );
99- }
72+ root.insert (" agent" , agent);
73+
74+ return root;
10075}
10176
102- bool CustomAuthStep::parseResponse ()
77+ void CustomAuthStep::onRequestDone ()
10378{
10479 qCDebug (authCredentials ()) << *m_response;
105- if (m_request->error () != QNetworkReply::NoError) {
106- qWarning () << " Reply error:" << m_request->error ();
107- return false ;
80+
81+ if (m_request->error () != QNetworkReply::NoError && m_request->error () != QNetworkReply::ContentAccessDenied) {
82+ emit finished (AccountTaskState::STATE_OFFLINE, m_request->errorString ());
83+ return ;
10884 }
10985
110- auto jsonResponse = QJsonDocument::fromJson (*m_response);
86+ QJsonParseError err;
87+ auto jsonResponse = QJsonDocument::fromJson (*m_response, &err);
88+
89+ if (err.error != QJsonParseError::NoError) {
90+ emit finished (AccountTaskState::STATE_FAILED_SOFT, tr (" Error while parsing JSON response: %1" ).arg (err.errorString ()));
91+ return ;
92+ }
93+
94+ if (m_request->error () == QNetworkReply::ContentAccessDenied) {
95+ const QString msg = jsonResponse[" errorMessage" ].toString () == " Invalid credentials. Invalid username or password."
96+ ? tr (" Invalid credentials. Invalid username or password." )
97+ : m_request->errorString ();
98+
99+ emit finished (AccountTaskState::STATE_FAILED_HARD, msg);
100+ return ;
101+ }
111102
112103 m_data->yggdrasilToken .token = jsonResponse[" accessToken" ].toString ();
104+ m_data->yggdrasilToken .validity = Validity::Certain;
105+ m_data->yggdrasilToken .issueInstant = QDateTime::currentDateTimeUtc ();
113106
114107 m_data->clientID = jsonResponse[" clientToken" ].toString ();
115108
116- if (!jsonResponse[" selectedProfile" ].isNull ()) {
117- auto profile = jsonResponse[" selectedProfile" ].toObject ();
118- m_data->minecraftProfile .id = profile[" id" ].toString ();
119- m_data->minecraftProfile .name = profile[" name" ].toString ();
109+ QJsonObject selectedProfile = jsonResponse[" selectedProfile" ].toObject ();
110+ if (!selectedProfile.isEmpty ()) {
111+ m_data->minecraftProfile .id = selectedProfile[" id" ].toString ();
112+ m_data->minecraftProfile .name = selectedProfile[" name" ].toString ();
113+
114+ emit finished (AccountTaskState::STATE_WORKING, tr (" Got authorization for custom account" ));
115+ return ;
120116 }
121117
122118 const QJsonArray profiles = jsonResponse[" availableProfiles" ].toArray ();
123- if (profiles.size () > 1 && m_data-> minecraftProfile . id . isEmpty () ) {
119+ if (profiles.size () > 1 ) {
124120 const auto profileName = [](const auto & profile) {
125121 auto obj = profile.toObject ();
126122 return obj[" name" ].toString ();
@@ -134,32 +130,25 @@ bool CustomAuthStep::parseResponse()
134130 QInputDialog::getItem (nullptr , tr (" Select profile" ), tr (" Select profile for this account" ), list, 0 , false , &ok);
135131
136132 if (!ok) {
137- return false ;
133+ emit finished (AccountTaskState::STATE_FAILED_SOFT, tr (" Profile selection cancelled" ));
134+ return ;
138135 }
139136
140137 const auto it = std::ranges::find (profiles, selectedProfileName, profileName);
141138 if (it != profiles.end ()) {
142139 auto profileObj = it->toObject ();
143140 m_data->minecraftProfile = MinecraftProfile{ .id = profileObj[" id" ].toString (), .name = profileObj[" name" ].toString () };
144141 } else {
145- return false ;
142+ // assuming that this will never happen
143+ emit finished (AccountTaskState::STATE_FAILED_SOFT, tr (" Something went wrong" ));
144+ return ;
146145 }
147- }
148-
149- if (profiles.size () == 1 && m_data->minecraftProfile .id .isEmpty ()) {
146+ } else if (profiles.size () == 1 ) {
150147 auto profileObj = profiles.first ().toObject ();
151148 m_data->minecraftProfile = MinecraftProfile{ .id = profileObj[" id" ].toString (), .name = profileObj[" name" ].toString () };
152149 }
153150
154- return true ;
155- }
151+ m_data->profileSelectedExplicitly = true ;
156152
157- void CustomAuthStep::onRequestDone ()
158- {
159- if (!parseResponse ()) {
160- emit finished (AccountTaskState::STATE_OFFLINE,
161- tr (" Failed to get authorization for %1 account: %2" ).arg (authType (), m_request->errorString ()));
162- return ;
163- }
164- emit finished (AccountTaskState::STATE_WORKING, tr (" Got authorization for %1 account" ).arg (authType ()));
153+ emit finished (AccountTaskState::STATE_WORKING, tr (" Got authorization for custom account" ));
165154}
0 commit comments