diff options
author | Petr Mrázek <peterix@gmail.com> | 2021-08-22 20:01:18 +0200 |
---|---|---|
committer | Petr Mrázek <peterix@gmail.com> | 2021-08-22 20:01:18 +0200 |
commit | eae65da110bb957194b34e0f3573ce4e6e6ddc78 (patch) | |
tree | 425a9252338c3db5eac4a5018f594ab646f6cea6 /launcher | |
parent | 50b92c1af2d28ab59a9b70ce0b3dca62bf1a7583 (diff) | |
download | PrismLauncher-eae65da110bb957194b34e0f3573ce4e6e6ddc78.tar.gz PrismLauncher-eae65da110bb957194b34e0f3573ce4e6e6ddc78.tar.bz2 PrismLauncher-eae65da110bb957194b34e0f3573ce4e6e6ddc78.zip |
GH-3392 Switch MS account login to use device flow instead
Device flow involves the user manually opening a web page and putting in
a code. We no longer need to interact with the browser.
Diffstat (limited to 'launcher')
-rw-r--r-- | launcher/dialogs/MSALoginDialog.cpp | 34 | ||||
-rw-r--r-- | launcher/dialogs/MSALoginDialog.h | 10 | ||||
-rw-r--r-- | launcher/dialogs/MSALoginDialog.ui | 3 | ||||
-rw-r--r-- | launcher/minecraft/auth/AccountTask.h | 4 | ||||
-rw-r--r-- | launcher/minecraft/auth/flows/AuthContext.cpp | 46 | ||||
-rw-r--r-- | launcher/minecraft/auth/flows/AuthContext.h | 14 |
6 files changed, 82 insertions, 29 deletions
diff --git a/launcher/dialogs/MSALoginDialog.cpp b/launcher/dialogs/MSALoginDialog.cpp index 14a0e243..989a4c9f 100644 --- a/launcher/dialogs/MSALoginDialog.cpp +++ b/launcher/dialogs/MSALoginDialog.cpp @@ -41,6 +41,9 @@ int MSALoginDialog::exec() { connect(m_loginTask.get(), &Task::succeeded, this, &MSALoginDialog::onTaskSucceeded); connect(m_loginTask.get(), &Task::status, this, &MSALoginDialog::onTaskStatus); connect(m_loginTask.get(), &Task::progress, this, &MSALoginDialog::onTaskProgress); + connect(m_loginTask.get(), &AccountTask::showVerificationUriAndCode, this, &MSALoginDialog::showVerificationUriAndCode); + connect(m_loginTask.get(), &AccountTask::hideVerificationUriAndCode, this, &MSALoginDialog::hideVerificationUriAndCode); + connect(&m_externalLoginTimer, &QTimer::timeout, this, &MSALoginDialog::externalLoginTick); m_loginTask->start(); return QDialog::exec(); @@ -52,6 +55,37 @@ MSALoginDialog::~MSALoginDialog() delete ui; } +void MSALoginDialog::externalLoginTick() { + m_externalLoginElapsed++; + ui->progressBar->setValue(m_externalLoginElapsed); + ui->progressBar->repaint(); + + if(m_externalLoginElapsed >= m_externalLoginTimeout) { + m_externalLoginTimer.stop(); + } +} + + +void MSALoginDialog::showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn) { + m_externalLoginElapsed = 0; + m_externalLoginTimeout = expiresIn; + + m_externalLoginTimer.setInterval(1000); + m_externalLoginTimer.setSingleShot(false); + m_externalLoginTimer.start(); + + ui->progressBar->setMaximum(expiresIn); + ui->progressBar->setValue(m_externalLoginElapsed); + + QString urlString = uri.toString(); + QString linkString = QString("<a href=\"%1\">%2</a>").arg(urlString, urlString); + ui->label->setText(tr("<p>Please open up %1 in a browser and put in the code <b>%2</b> to proceed with login.</p>").arg(linkString, code)); +} + +void MSALoginDialog::hideVerificationUriAndCode() { + m_externalLoginTimer.stop(); +} + void MSALoginDialog::setUserInputsEnabled(bool enable) { ui->buttonBox->setEnabled(enable); diff --git a/launcher/dialogs/MSALoginDialog.h b/launcher/dialogs/MSALoginDialog.h index 402180ee..3d26a0dd 100644 --- a/launcher/dialogs/MSALoginDialog.h +++ b/launcher/dialogs/MSALoginDialog.h @@ -17,6 +17,7 @@ #include <QtWidgets/QDialog> #include <QtCore/QEventLoop> +#include <QTimer> #include "minecraft/auth/MinecraftAccount.h" @@ -46,10 +47,17 @@ slots: void onTaskSucceeded(); void onTaskStatus(const QString &status); void onTaskProgress(qint64 current, qint64 total); + void showVerificationUriAndCode(const QUrl &uri, const QString &code, int expiresIn); + void hideVerificationUriAndCode(); + + void externalLoginTick(); private: Ui::MSALoginDialog *ui; MinecraftAccountPtr m_account; - std::shared_ptr<Task> m_loginTask; + std::shared_ptr<AccountTask> m_loginTask; + QTimer m_externalLoginTimer; + int m_externalLoginElapsed = 0; + int m_externalLoginTimeout = 0; }; diff --git a/launcher/dialogs/MSALoginDialog.ui b/launcher/dialogs/MSALoginDialog.ui index 4d82fe2b..78cbfb26 100644 --- a/launcher/dialogs/MSALoginDialog.ui +++ b/launcher/dialogs/MSALoginDialog.ui @@ -30,6 +30,9 @@ aaaaa</string> <property name="textFormat"> <enum>Qt::RichText</enum> </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> diff --git a/launcher/minecraft/auth/AccountTask.h b/launcher/minecraft/auth/AccountTask.h index 3f08096f..fc3488eb 100644 --- a/launcher/minecraft/auth/AccountTask.h +++ b/launcher/minecraft/auth/AccountTask.h @@ -83,6 +83,10 @@ public: return m_accountState; } +signals: + void showVerificationUriAndCode(const QUrl &uri, const QString &code, int expiresIn); + void hideVerificationUriAndCode(); + protected: /** diff --git a/launcher/minecraft/auth/flows/AuthContext.cpp b/launcher/minecraft/auth/flows/AuthContext.cpp index 9754d1a9..ecd7e310 100644 --- a/launcher/minecraft/auth/flows/AuthContext.cpp +++ b/launcher/minecraft/auth/flows/AuthContext.cpp @@ -43,7 +43,7 @@ void AuthContext::finishActivity() { throw 0; } m_activity = Katabasis::Activity::Idle; - m_stage = MSAStage::Idle; + setStage(AuthStage::Complete); m_data->validity_ = m_data->minecraftProfile.validity; emit activityChanged(m_activity); } @@ -55,16 +55,16 @@ void AuthContext::initMSA() { Katabasis::OAuth2::Options opts; opts.scope = "XboxLive.signin offline_access"; opts.clientIdentifier = BuildConfig.MSA_CLIENT_ID; - opts.authorizationUrl = "https://login.live.com/oauth20_authorize.srf"; - opts.accessTokenUrl = "https://login.live.com/oauth20_token.srf"; + opts.authorizationUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode"; + opts.accessTokenUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"; opts.listenerPorts = {28562, 28563, 28564, 28565, 28566}; m_oauth2 = new OAuth2(opts, m_data->msaToken, this, mgr); + m_oauth2->setGrantFlow(Katabasis::OAuth2::GrantFlowDevice); connect(m_oauth2, &OAuth2::linkingFailed, this, &AuthContext::onOAuthLinkingFailed); connect(m_oauth2, &OAuth2::linkingSucceeded, this, &AuthContext::onOAuthLinkingSucceeded); - connect(m_oauth2, &OAuth2::openBrowser, this, &AuthContext::onOpenBrowser); - connect(m_oauth2, &OAuth2::closeBrowser, this, &AuthContext::onCloseBrowser); + connect(m_oauth2, &OAuth2::showVerificationUriAndCode, this, &AuthContext::showVerificationUriAndCode); connect(m_oauth2, &OAuth2::activityChanged, this, &AuthContext::onOAuthActivityChanged); } @@ -106,20 +106,14 @@ bool AuthContext::signOut() { } */ -void AuthContext::onOpenBrowser(const QUrl &url) { - QDesktopServices::openUrl(url); -} - -void AuthContext::onCloseBrowser() { - -} - void AuthContext::onOAuthLinkingFailed() { + emit hideVerificationUriAndCode(); finishActivity(); changeState(STATE_FAILED_HARD, tr("Microsoft user authentication failed.")); } void AuthContext::onOAuthLinkingSucceeded() { + emit hideVerificationUriAndCode(); auto *o2t = qobject_cast<OAuth2 *>(sender()); if (!o2t->linked()) { finishActivity(); @@ -143,7 +137,7 @@ void AuthContext::onOAuthActivityChanged(Katabasis::Activity activity) { } void AuthContext::doUserAuth() { - m_stage = MSAStage::UserAuth; + setStage(AuthStage::UserAuth); changeState(STATE_WORKING, tr("Starting user authentication")); QString xbox_auth_template = R"XXX( @@ -307,7 +301,7 @@ void AuthContext::onUserAuthDone( } m_data->userToken = temp; - m_stage = MSAStage::XboxAuth; + setStage(AuthStage::XboxAuth); changeState(STATE_WORKING, tr("Starting XBox authentication")); doSTSAuthMinecraft(); @@ -671,7 +665,7 @@ bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) { } void AuthContext::doMinecraftProfile() { - m_stage = MSAStage::MinecraftProfile; + setStage(AuthStage::MinecraftProfile); changeState(STATE_WORKING, tr("Starting minecraft profile acquisition")); auto url = QUrl("https://api.minecraftservices.com/minecraft/profile"); @@ -710,7 +704,7 @@ void AuthContext::onMinecraftProfileDone(int, QNetworkReply::NetworkError error, } void AuthContext::doGetSkin() { - m_stage = MSAStage::Skin; + setStage(AuthStage::Skin); changeState(STATE_WORKING, tr("Fetching player skin")); auto url = QUrl(m_data->minecraftProfile.skin.url); @@ -730,12 +724,18 @@ void AuthContext::onSkinDone(int, QNetworkReply::NetworkError error, QByteArray changeState(STATE_SUCCEEDED, tr("Finished all authentication steps")); } +void AuthContext::setStage(AuthContext::AuthStage stage) { + m_stage = stage; + emit progress((int)m_stage, (int)AuthStage::Complete); +} + + QString AuthContext::getStateMessage() const { switch (m_accountState) { case STATE_WORKING: switch(m_stage) { - case MSAStage::Idle: { + case AuthStage::Initial: { QString loginMessage = tr("Logging in as %1 user"); if(m_data->type == AccountType::MSA) { return loginMessage.arg("Microsoft"); @@ -744,14 +744,16 @@ QString AuthContext::getStateMessage() const { return loginMessage.arg("Mojang"); } } - case MSAStage::UserAuth: + case AuthStage::UserAuth: return tr("Logging in as XBox user"); - case MSAStage::XboxAuth: + case AuthStage::XboxAuth: return tr("Logging in with XBox and Mojang services"); - case MSAStage::MinecraftProfile: + case AuthStage::MinecraftProfile: return tr("Getting Minecraft profile"); - case MSAStage::Skin: + case AuthStage::Skin: return tr("Getting Minecraft skin"); + case AuthStage::Complete: + return tr("Finished"); default: break; } diff --git a/launcher/minecraft/auth/flows/AuthContext.h b/launcher/minecraft/auth/flows/AuthContext.h index 5f99dba3..1d9f8f72 100644 --- a/launcher/minecraft/auth/flows/AuthContext.h +++ b/launcher/minecraft/auth/flows/AuthContext.h @@ -36,8 +36,7 @@ private slots: // OAuth-specific callbacks void onOAuthLinkingSucceeded(); void onOAuthLinkingFailed(); - void onOpenBrowser(const QUrl &url); - void onCloseBrowser(); + void onOAuthActivityChanged(Katabasis::Activity activity); // Yggdrasil specific callbacks @@ -82,13 +81,16 @@ protected: bool m_xboxProfileSucceeded = false; bool m_mcAuthSucceeded = false; Katabasis::Activity m_activity = Katabasis::Activity::Idle; - enum class MSAStage { - Idle, + enum class AuthStage { + Initial, UserAuth, XboxAuth, MinecraftProfile, - Skin - } m_stage = MSAStage::Idle; + Skin, + Complete + } m_stage = AuthStage::Initial; + + void setStage(AuthStage stage); QNetworkAccessManager *mgr = nullptr; }; |