Authorization complete. You may close this " + "window.
")); + if (!m_replyHandler->isListening() && + !m_replyHandler->listen(QHostAddress::LocalHost, NexusOAuth::redirectPort())) { + handleError(QObject::tr("Failed to bind to localhost on port %1.") + .arg(NexusOAuth::redirectPort())); + return; + } + + m_flow->setReplyHandler(m_replyHandler.get()); + + QObject::connect(m_flow.get(), &QAbstractOAuth::authorizeWithBrowser, this, + [&](const QUrl& url) { + shell::Open(url); + setState(State::WaitingForBrowser); + }); + + QObject::connect(m_flow.get(), &QAbstractOAuth::statusChanged, this, + [&](QAbstractOAuth::Status status) { + switch (status) { + case QAbstractOAuth::Status::RefreshingToken: + case QAbstractOAuth::Status::TemporaryCredentialsReceived: + setState(State::Authorizing); + break; + + case QAbstractOAuth::Status::Granted: + setState(State::Finished); + break; + + default: + break; + } + }); + + QObject::connect(m_flow.get(), &QAbstractOAuth::requestFailed, this, + [&](QAbstractOAuth::Error error) { + handleError(QObject::tr("Authorization failed (%1)").arg(int(error))); + }); + + QObject::connect(m_flow.get(), &QAbstractOAuth::granted, this, [&] { + notifyTokens(); + }); + + m_active = true; + setState(State::Initializing); + m_flow->grant(); +} + +void NexusOAuthLogin::cancel() +{ + if (m_replyHandler) { + m_replyHandler->close(); + } + + m_flow.reset(); + m_replyHandler.reset(); + if (m_active) { + m_active = false; + setState(State::Cancelled); + } +} + +bool NexusOAuthLogin::isActive() const +{ + return m_active; +} + +void NexusOAuthLogin::setState(State state, const QString& message) +{ + if (stateChanged) { + stateChanged(state, message); + } +} + +void NexusOAuthLogin::notifyTokens() +{ + if (!m_flow) { + handleError(QObject::tr("Internal error: OAuth flow is missing.")); + return; + } + + QJsonObject payload; + payload.insert(QStringLiteral("access_token"), m_flow->token()); + + const auto extras = m_flow->extraTokens(); + for (auto it = extras.constBegin(); it != extras.constEnd(); ++it) { + payload.insert(it.key(), QJsonValue::fromVariant(it.value())); + } + + auto tokens = makeTokensFromResponse(payload); + if (!tokens.isValid()) { + handleError(QObject::tr("Invalid OAuth token payload.")); + return; + } + + tokens.scope = m_flow->scope(); + + m_flow.reset(); + m_replyHandler.reset(); + m_codeVerifier.clear(); + m_active = false; + if (tokensReceived) { + tokensReceived(tokens); + } +} + +void NexusOAuthLogin::handleError(const QString& message) +{ + if (m_replyHandler) { + m_replyHandler->close(); + } + m_flow.reset(); + m_replyHandler.reset(); + m_codeVerifier.clear(); + m_active = false; + if (stateChanged) { + stateChanged(State::Error, message); + } +} + +namespace +{ +QByteArray randomBytes(int length) +{ + QByteArray bytes; + bytes.resize(length); + QRandomGenerator::system()->generate(bytes.begin(), bytes.end()); + return bytes; +} +} + +void NexusOAuthLogin::injectPkceChallenge(QAbstractOAuth::Stage stage, + QMultiMapAuthorization complete. You may close this "
@@ -143,14 +140,13 @@ void NexusOAuthLogin::start()
QObject::connect(m_flow.get(), &QOAuth2AuthorizationCodeFlow::requestFailed, this,
[&](QAbstractOAuth::Error error) {
- handleError(QObject::tr("Authorization failed (%1)").arg(int(error)));
+ handleError(
+ QObject::tr("Authorization failed (%1)").arg(int(error)));
});
- QObject::connect(m_flow.get(),
- &QOAuth2AuthorizationCodeFlow::granted, this,
- [&]() {
- notifyTokens();
- });
+ QObject::connect(m_flow.get(), &QOAuth2AuthorizationCodeFlow::granted, this, [&]() {
+ notifyTokens();
+ });
m_active = true;
setState(State::Initializing);
@@ -239,7 +235,7 @@ QByteArray randomBytes(int length)
QRandomGenerator::system()->generate(bytes.begin(), bytes.end());
return bytes;
}
-}
+} // namespace
void NexusOAuthLogin::injectPkceChallenge(QAbstractOAuth::Stage stage,
QMultiMap
Authorization complete. You may close this " - "window.
")); - if (!m_replyHandler->isListening() && - !m_replyHandler->listen(QHostAddress::LocalHost, NexusOAuth::redirectPort())) { - handleError(QObject::tr("Failed to bind to localhost on port %1.") - .arg(NexusOAuth::redirectPort())); - return; - } - - m_flow->setReplyHandler(m_replyHandler.get()); - - QObject::connect(m_flow.get(), &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, - this, [&](const QUrl& url) { - shell::Open(url); - setState(State::WaitingForBrowser); - }); - - QObject::connect(m_flow.get(), &QOAuth2AuthorizationCodeFlow::statusChanged, this, - [&](QAbstractOAuth::Status status) { - switch (status) { - case QAbstractOAuth::Status::RefreshingToken: - case QAbstractOAuth::Status::TemporaryCredentialsReceived: - setState(State::Authorizing); - break; - - case QAbstractOAuth::Status::Granted: - setState(State::Finished); - break; - - default: - break; - } - }); - - QObject::connect(m_flow.get(), &QOAuth2AuthorizationCodeFlow::requestFailed, this, - [&](QAbstractOAuth::Error error) { - handleError( - QObject::tr("Authorization failed (%1)").arg(int(error))); - }); - - QObject::connect(m_flow.get(), &QOAuth2AuthorizationCodeFlow::granted, this, [&]() { - notifyTokens(); - }); + accessManager->tokensReceived = tokensReceived; + accessManager->stateChanged = stateChanged; + NexusInterface::instance().getAccessManager()->connectOrRefresh(NexusOAuthTokens()); m_active = true; - setState(State::Initializing); - m_flow->grant(); -} - -void NexusOAuthLogin::cancel() -{ - if (m_replyHandler) { - m_replyHandler->close(); - } - - m_flow.reset(); - m_replyHandler.reset(); - if (m_active) { - m_active = false; - setState(State::Cancelled); - } -} - -bool NexusOAuthLogin::isActive() const -{ - return m_active; -} - -void NexusOAuthLogin::setState(State state, const QString& message) -{ - if (stateChanged) { - stateChanged(state, message); - } } -void NexusOAuthLogin::notifyTokens() +void NexusOAuthLogin::authorizationEnded() { - if (!m_flow) { - handleError(QObject::tr("Internal error: OAuth flow is missing.")); - return; - } - - QVariantMap payload; - payload["access_token"] = m_flow->token(); - payload["refresh_token"] = m_flow->refreshToken(); - payload["scope"] = m_flow->scope(); - payload["expiration_at"] = m_flow->expirationAt(); - - const auto extras = m_flow->extraTokens(); - payload.insert(extras); - - auto tokens = makeTokensFromResponse(payload); - if (!tokens.isValid()) { - handleError(QObject::tr("Invalid OAuth token payload.")); - return; - } - - tokens.scope = m_flow->scope(); - - m_flow.reset(); - m_replyHandler.reset(); - m_codeVerifier.clear(); m_active = false; - if (tokensReceived) { - tokensReceived(tokens); - } } -void NexusOAuthLogin::handleError(const QString& message) +void NexusOAuthLogin::cancel() { - if (m_replyHandler) { - m_replyHandler->close(); - } - m_flow.reset(); - m_replyHandler.reset(); - m_codeVerifier.clear(); + NexusInterface::instance().getAccessManager()->cancelAuth(); m_active = false; - if (stateChanged) { - stateChanged(State::Error, message); - } } -namespace -{ -QByteArray randomBytes(int length) -{ - QByteArray bytes; - bytes.resize(length); - QRandomGenerator::system()->generate(bytes.begin(), bytes.end()); - return bytes; -} -} // namespace - -void NexusOAuthLogin::injectPkceChallenge(QAbstractOAuth::Stage stage, - QMultiMapAuthorization complete. You " + "may close this " + "window.
")); + if (!m_NexusOAuthReplyHandler->isListening() && + !m_NexusOAuthReplyHandler->listen(QHostAddress::LocalHost, + NexusOAuth::redirectPort())) { + handleOAuthError(QObject::tr("Failed to bind to localhost on port %1.") + .arg(NexusOAuth::redirectPort())); + return; + } + m_NexusOAuth->setReplyHandler(m_NexusOAuthReplyHandler.get()); + if (!tokens.accessToken.isEmpty()) { + m_NexusOAuth->setToken(tokens.accessToken); + m_NexusOAuth->setRefreshToken(tokens.refreshToken); + scope.clear(); + for (const QString scopeItem : tokens.scope.split(" ")) { + scope.insert(scopeItem.toUtf8()); + } + m_NexusOAuth->setRequestedScopeTokens(scope); + + setOAuthState(OAuthState::Refreshing); + m_NexusOAuth->refreshTokens(); + } else { + + setOAuthState(OAuthState::Initializing); + m_NexusOAuth->grant(); + } +} + +void NXMAccessManager::cancelAuth() +{ + if (m_NexusOAuthReplyHandler) { + m_NexusOAuthReplyHandler->close(); + } + + m_NexusOAuth.reset(); + m_NexusOAuthReplyHandler.reset(); + setOAuthState(OAuthState::Cancelled); +} + +QNetworkReply* NXMAccessManager::makeOAuthGetRequest(const QUrl url) +{ + if (!m_NexusOAuth->token().isEmpty()) { + QNetworkRequest request(url); + m_NexusOAuth->prepareRequest(&request, "GET"); + request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, + userAgent().toUtf8()); + request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, + "application/json"); + request.setRawHeader("Protocol-Version", "1.0.0"); + request.setRawHeader("Application-Name", "MO2"); + request.setRawHeader("Application-Version", MOVersion().toUtf8()); + return m_NexusOAuth->networkAccessManager()->get(request); + } + return nullptr; +} + +QNetworkReply* NXMAccessManager::makeOAuthPostRequest(const QUrl url, + const QByteArray payload = {}) +{ + if (!m_NexusOAuth->token().isEmpty()) { + QNetworkRequest request(url); + m_NexusOAuth->prepareRequest(&request, "POST", payload); + request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, + userAgent().toUtf8()); + request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, + "application/json"); + request.setRawHeader("Protocol-Version", "1.0.0"); + request.setRawHeader("Application-Name", "MO2"); + request.setRawHeader("Application-Version", MOVersion().toUtf8()); + return m_NexusOAuth->networkAccessManager()->post(request, payload); + } + return nullptr; } void NXMAccessManager::apiCheck(const NexusOAuthTokens& tokens, bool force) { - if (m_validator.isActive()) { + if (m_Validator.isActive()) { return; } setTokens(tokens); if (m_Settings && m_Settings->network().offlineMode()) { - m_validationState = NotChecked; + m_ValidationState = NotChecked; return; } if (force) { - m_validationState = NotChecked; + m_ValidationState = NotChecked; } - if (m_validationState == Valid) { + if (m_ValidationState == Valid) { emit validateSuccessful(false); return; } + if (m_NexusOAuth->token().isEmpty() && !tokens.accessToken.isEmpty()) { + tokensReceived = [&](const NexusOAuthTokens& tokens) { + saveRefreshedTokens(tokens); + }; + stateChanged = nullptr; + connectOrRefresh(tokens); + } startValidationCheck(tokens); } @@ -914,15 +1067,28 @@ QString NXMAccessManager::userAgent(const QString& subModule) const void NXMAccessManager::clearTokens() { - m_validator.cancel(); - m_tokens.reset(); + m_Validator.cancel(); + // TODO: Verify revocation process + // if (m_Tokens && !m_Tokens->accessToken.isEmpty()) { + // QNetworkRequest request(NexusOAuth::tokenUrl()); + // QUrlQuery params; + // params.addQueryItem("token", m_Tokens->refreshToken); + // params.addQueryItem("token_type_hint", "refresh_token"); + // m_NexusOAuth->prepareRequest(&request, "POST", + // params.toString(QUrl::FullyEncoded).toUtf8()); + // m_NexusOAuth->networkAccessManager()->post( + // request, params.toString(QUrl::FullyEncoded).toUtf8()); + //} + m_Tokens.reset(); + m_NexusOAuthReplyHandler.reset(); + m_NexusOAuth.reset(); emit credentialsReceived(APIUserAccount()); } void NXMAccessManager::startProgress() { if (!m_ProgressDialog) { - m_ProgressDialog.reset(new ValidationProgressDialog(m_Settings, m_validator)); + m_ProgressDialog.reset(new ValidationProgressDialog(m_Settings, m_Validator)); } m_ProgressDialog->start(); diff --git a/src/nxmaccessmanager.h b/src/nxmaccessmanager.h index 62f0a2065..d89752ff4 100644 --- a/src/nxmaccessmanager.h +++ b/src/nxmaccessmanager.h @@ -29,6 +29,8 @@ along with Mod Organizer. If not, seeAuthorization complete. You "
@@ -970,7 +967,6 @@ void NXMAccessManager::connectOrRefresh(const NexusOAuthTokens tokens)
setOAuthState(OAuthState::Refreshing);
m_NexusOAuth->refreshTokens();
} else {
-
setOAuthState(OAuthState::Initializing);
m_NexusOAuth->grant();
}
@@ -1073,10 +1069,6 @@ void NXMAccessManager::apiCheck(const NexusOAuthTokens& tokens, bool force)
}
if (m_NexusOAuth->token().isEmpty() && !tokens.accessToken.isEmpty()) {
- tokensReceived = [&](const NexusOAuthTokens& tokens) {
- saveRefreshedTokens(tokens);
- };
- stateChanged = nullptr;
connectOrRefresh(tokens);
} else if (!tokens.apiKey.isEmpty()) {
startValidationCheck(tokens);
diff --git a/src/nxmaccessmanager.h b/src/nxmaccessmanager.h
index 75a2fa758..96b5ec059 100644
--- a/src/nxmaccessmanager.h
+++ b/src/nxmaccessmanager.h
@@ -198,9 +198,6 @@ class NXMAccessManager : public QNetworkAccessManager
static QString stateToString(OAuthState state, const QString& details = {});
- std::function Authorization complete. You "
"may close this "
"window.Mod Organizer