diff options
Diffstat (limited to 'logic/auth')
-rw-r--r-- | logic/auth/AuthSession.cpp | 30 | ||||
-rw-r--r-- | logic/auth/AuthSession.h | 49 | ||||
-rw-r--r-- | logic/auth/MojangAccount.cpp | 82 | ||||
-rw-r--r-- | logic/auth/MojangAccount.h | 38 | ||||
-rw-r--r-- | logic/auth/YggdrasilTask.h | 17 | ||||
-rw-r--r-- | logic/auth/flows/RefreshTask.cpp | 4 | ||||
-rw-r--r-- | logic/auth/flows/RefreshTask.h | 3 |
7 files changed, 184 insertions, 39 deletions
diff --git a/logic/auth/AuthSession.cpp b/logic/auth/AuthSession.cpp new file mode 100644 index 00000000..8758bfbd --- /dev/null +++ b/logic/auth/AuthSession.cpp @@ -0,0 +1,30 @@ +#include "AuthSession.h" +#include <QJsonObject> +#include <QJsonArray> +#include <QJsonDocument> +#include <QStringList> + +QString AuthSession::serializeUserProperties() +{ + QJsonObject userAttrs; + for (auto key : u.properties.keys()) + { + auto array = QJsonArray::fromStringList(u.properties.values(key)); + userAttrs.insert(key, array); + } + QJsonDocument value(userAttrs); + return value.toJson(QJsonDocument::Compact); + +} + +bool AuthSession::MakeOffline(QString offline_playername) +{ + if (status != PlayableOffline && status != PlayableOnline) + { + return false; + } + session = "-"; + player_name = offline_playername; + status = PlayableOffline; + return true; +} diff --git a/logic/auth/AuthSession.h b/logic/auth/AuthSession.h new file mode 100644 index 00000000..2ac170fa --- /dev/null +++ b/logic/auth/AuthSession.h @@ -0,0 +1,49 @@ +#pragma once + +#include <QString> +#include <QMultiMap> +#include <memory> + +struct User +{ + QString id; + QMultiMap<QString, QString> properties; +}; + +struct AuthSession +{ + bool MakeOffline(QString offline_playername); + + QString serializeUserProperties(); + + enum Status + { + Undetermined, + RequiresPassword, + PlayableOffline, + PlayableOnline + } status = Undetermined; + + User u; + + // client token + QString client_token; + // account user name + QString username; + // combined session ID + QString session; + // volatile auth token + QString access_token; + // profile name + QString player_name; + // profile ID + QString uuid; + // 'legacy' or 'mojang', depending on account type + QString user_type; + // Did the auth server reply? + bool auth_server_online = false; + // Did the user request online mode? + bool wants_online = true; +}; + +typedef std::shared_ptr<AuthSession> AuthSessionPtr; diff --git a/logic/auth/MojangAccount.cpp b/logic/auth/MojangAccount.cpp index f41985ec..6c937ef1 100644 --- a/logic/auth/MojangAccount.cpp +++ b/logic/auth/MojangAccount.cpp @@ -24,6 +24,7 @@ #include <QJsonArray> #include <QRegExp> #include <QStringList> +#include <QJsonDocument> #include <logger/QsLog.h> @@ -165,15 +166,26 @@ AccountStatus MojangAccount::accountStatus() const { if (m_accessToken.isEmpty()) return NotVerified; - if (!m_online) + else return Verified; - return Online; } -std::shared_ptr<YggdrasilTask> MojangAccount::login(QString password) +std::shared_ptr<YggdrasilTask> MojangAccount::login(AuthSessionPtr session, + QString password) { - if (m_currentTask) - return m_currentTask; + Q_ASSERT(m_currentTask.get() == nullptr); + + // take care of the true offline status + if (accountStatus() == NotVerified && password.isEmpty()) + { + if (session) + { + session->status = AuthSession::RequiresPassword; + fillSession(session); + } + return nullptr; + } + if (password.isEmpty()) { m_currentTask.reset(new RefreshTask(this)); @@ -182,6 +194,8 @@ std::shared_ptr<YggdrasilTask> MojangAccount::login(QString password) { m_currentTask.reset(new AuthenticateTask(this, password)); } + m_currentTask->assignSession(session); + connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); return m_currentTask; @@ -189,24 +203,76 @@ std::shared_ptr<YggdrasilTask> MojangAccount::login(QString password) void MojangAccount::authSucceeded() { - m_online = true; + auto session = m_currentTask->getAssignedSession(); + if (session) + { + session->status = + session->wants_online ? AuthSession::PlayableOnline : AuthSession::PlayableOffline; + fillSession(session); + session->auth_server_online = true; + } m_currentTask.reset(); emit changed(); } void MojangAccount::authFailed(QString reason) { + auto session = m_currentTask->getAssignedSession(); // This is emitted when the yggdrasil tasks time out or are cancelled. // -> we treat the error as no-op if (reason == "Yggdrasil task cancelled.") { - // do nothing + if (session) + { + session->status = accountStatus() == Verified ? AuthSession::PlayableOffline + : AuthSession::RequiresPassword; + session->auth_server_online = false; + fillSession(session); + } } else { - m_online = false; m_accessToken = QString(); emit changed(); + if (session) + { + session->status = AuthSession::RequiresPassword; + session->auth_server_online = true; + fillSession(session); + } } m_currentTask.reset(); } + +void MojangAccount::fillSession(AuthSessionPtr session) +{ + // the user name. you have to have an user name + session->username = m_username; + // volatile auth token + session->access_token = m_accessToken; + // the semi-permanent client token + session->client_token = m_clientToken; + if (currentProfile()) + { + // profile name + session->player_name = currentProfile()->name; + // profile ID + session->uuid = currentProfile()->id; + // 'legacy' or 'mojang', depending on account type + session->user_type = currentProfile()->legacy ? "legacy" : "mojang"; + if (!session->access_token.isEmpty()) + { + session->session = "token:" + m_accessToken + ":" + m_profiles[m_currentProfile].id; + } + else + { + session->session = "-"; + } + } + else + { + session->player_name = "Player"; + session->session = "-"; + } + session->u = user(); +} diff --git a/logic/auth/MojangAccount.h b/logic/auth/MojangAccount.h index dd5d54ae..a0565e2c 100644 --- a/logic/auth/MojangAccount.h +++ b/logic/auth/MojangAccount.h @@ -23,6 +23,7 @@ #include <QMap> #include <memory> +#include "AuthSession.h" class Task; class YggdrasilTask; @@ -45,17 +46,10 @@ struct AccountProfile bool legacy; }; -struct User -{ - QString id; - QMultiMap<QString,QString> properties; -}; - enum AccountStatus { NotVerified, - Verified, - Online + Verified }; /** @@ -84,7 +78,7 @@ public: /* construction */ QJsonObject saveToJson() const; public: /* manipulation */ - /** + /** * Sets the currently selected profile to the profile with the given ID string. * If profileId is not in the list of available profiles, the function will simply return * false. @@ -95,12 +89,9 @@ public: /* manipulation */ * Attempt to login. Empty password means we use the token. * If the attempt fails because we already are performing some task, it returns false. */ - std::shared_ptr<YggdrasilTask> login(QString password = QString()); + std::shared_ptr<YggdrasilTask> login(AuthSessionPtr session, + QString password = QString()); - void downgrade() - { - m_online = false; - } public: /* queries */ const QString &username() const { @@ -122,19 +113,11 @@ public: /* queries */ return m_profiles; } - const User & user() + const User &user() { return m_user; } - //! Get the session ID required for legacy Minecraft versions - QString sessionId() const - { - if (m_currentProfile != -1 && !m_accessToken.isEmpty()) - return "token:" + m_accessToken + ":" + m_profiles[m_currentProfile].id; - return "-"; - } - //! Returns the currently selected profile (if none, returns nullptr) const AccountProfile *currentProfile() const; @@ -169,16 +152,17 @@ protected: /* variables */ // the user structure, whatever it is. User m_user; - // true when the account is verified - bool m_online = false; - // current task we are executing here std::shared_ptr<YggdrasilTask> m_currentTask; -private slots: +private +slots: void authSucceeded(); void authFailed(QString reason); +private: + void fillSession(AuthSessionPtr session); + public: friend class YggdrasilTask; friend class AuthenticateTask; diff --git a/logic/auth/YggdrasilTask.h b/logic/auth/YggdrasilTask.h index 85f5a1e1..4a87067a 100644 --- a/logic/auth/YggdrasilTask.h +++ b/logic/auth/YggdrasilTask.h @@ -36,6 +36,21 @@ public: explicit YggdrasilTask(MojangAccount * account, QObject *parent = 0); /** + * assign a session to this task. the session will be filled with required infomration + * upon completion + */ + void assignSession(AuthSessionPtr session) + { + m_session = session; + } + + /// get the assigned session for filling with information. + AuthSessionPtr getAssignedSession() + { + return m_session; + } + + /** * Class describing a Yggdrasil error response. */ struct Error @@ -117,4 +132,6 @@ protected: const int timeout_max = 10000; const int time_step = 50; + + AuthSessionPtr m_session; }; diff --git a/logic/auth/flows/RefreshTask.cpp b/logic/auth/flows/RefreshTask.cpp index f63c736e..5a55ed91 100644 --- a/logic/auth/flows/RefreshTask.cpp +++ b/logic/auth/flows/RefreshTask.cpp @@ -25,8 +25,7 @@ #include "logger/QsLog.h" -RefreshTask::RefreshTask(MojangAccount *account, QObject *parent) - : YggdrasilTask(account, parent) +RefreshTask::RefreshTask(MojangAccount *account) : YggdrasilTask(account) { } @@ -126,7 +125,6 @@ bool RefreshTask::processResponse(QJsonObject responseData) m_account->m_user = u; } - // We've made it through the minefield of possible errors. Return true to indicate that // we've succeeded. QLOG_DEBUG() << "Finished reading refresh response."; diff --git a/logic/auth/flows/RefreshTask.h b/logic/auth/flows/RefreshTask.h index 2fd50c60..0dadc025 100644 --- a/logic/auth/flows/RefreshTask.h +++ b/logic/auth/flows/RefreshTask.h @@ -30,7 +30,7 @@ class RefreshTask : public YggdrasilTask { Q_OBJECT public: - RefreshTask(MojangAccount * account, QObject *parent = 0); + RefreshTask(MojangAccount * account); protected: virtual QJsonObject getRequestContent() const; @@ -41,3 +41,4 @@ protected: QString getStateMessage(const YggdrasilTask::State state) const; }; + |