aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-05-02 00:18:53 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-05-02 00:18:53 -0500
commit44515159af50756ff26a4996ca1526f7e51e9e98 (patch)
tree8e7c012235bf9456011f13f24bf5bde9222fbe51
parent73fc2ae731a4ef972c16efb31a30da9e9a8645c6 (diff)
nxmaccessmanager: derive legacy API key from OAuth session
Some plugins (Plugin Browser for MO2 and a number of older Python plugins) read only the legacy `apiKey` field. The OAuth login flow populates `accessToken` but never the legacy key, so those plugins fail with "User API Key is missing" even after a successful sign-in. Nexus' v1 `/users/validate.json` endpoint accepts the OAuth bearer token and returns the user's personal API key in the `key` field. We hit it once after `notifyTokens()` (fresh login) and once via the constructor queued call (already-logged-in users on first launch with this build), then persist the result through `GlobalSettings::setNexusApiKey()`. The fetch is a no-op when an apiKey is already present (manual entry or prior session) so it never overwrites a user-set value, and silently fails on non-200 / missing field so we don't break the OAuth-only path when Nexus eventually deprecates personal API keys. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
-rw-r--r--src/src/nxmaccessmanager.cpp58
-rw-r--r--src/src/nxmaccessmanager.h13
2 files changed, 71 insertions, 0 deletions
diff --git a/src/src/nxmaccessmanager.cpp b/src/src/nxmaccessmanager.cpp
index 18d83e1..a04ea3d 100644
--- a/src/src/nxmaccessmanager.cpp
+++ b/src/src/nxmaccessmanager.cpp
@@ -677,6 +677,14 @@ NXMAccessManager::NXMAccessManager(QObject* parent, Settings* s,
setCookieJar(new PersistentCookieJar(QDir::fromNativeSeparators(
m_Settings->paths().cache() + "/nexus_cookies.dat")));
}
+
+ // Migrate users who only have an OAuth token but no legacy API key — some
+ // plugins still read only the legacy field. Defer to the event loop so
+ // startup isn't blocked on the network.
+ if (!m_Tokens.accessToken.isEmpty() && m_Tokens.apiKey.isEmpty()) {
+ QMetaObject::invokeMethod(
+ this, [this]() { fetchAndStoreLegacyApiKey(); }, Qt::QueuedConnection);
+ }
}
void NXMAccessManager::setTopLevelWidget(QWidget* w)
@@ -782,6 +790,56 @@ void NXMAccessManager::notifyTokens()
startValidationCheck(tokens);
emit authorizationEnded();
+
+ // OAuth alone doesn't populate the legacy `apiKey`. Some plugins still
+ // only read that field; fetch the user's personal key from v1/validate
+ // and persist it so they don't error with "User API Key is missing".
+ fetchAndStoreLegacyApiKey();
+}
+
+void NXMAccessManager::fetchAndStoreLegacyApiKey()
+{
+ if (!m_Tokens.apiKey.isEmpty()) {
+ // already have one (manually entered or carried over)
+ return;
+ }
+ if (m_Tokens.accessToken.isEmpty()) {
+ return;
+ }
+
+ QNetworkRequest request(NexusV1BaseUrl + "users/validate.json");
+ request.setRawHeader("Authorization",
+ ("Bearer " + m_Tokens.accessToken).toUtf8());
+ addAPIHeaders(request);
+ QNetworkReply* reply = get(request);
+ if (!reply) {
+ log::debug("nexus: cannot fetch legacy API key, request creation failed");
+ return;
+ }
+
+ QObject::connect(reply, &QNetworkReply::finished, this, [this, reply]() {
+ reply->deleteLater();
+
+ const auto code =
+ reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ if (code != 200) {
+ log::debug("nexus: legacy API key fetch returned HTTP {}", code);
+ return;
+ }
+
+ const auto doc = QJsonDocument::fromJson(reply->readAll());
+ const QJsonObject obj = doc.object();
+ const QString key = obj.value("key").toString();
+ if (key.isEmpty()) {
+ log::debug("nexus: legacy API key not present in validate response");
+ return;
+ }
+
+ if (GlobalSettings::setNexusApiKey(key)) {
+ m_Tokens.apiKey = key;
+ log::info("nexus: stored legacy API key derived from OAuth session");
+ }
+ });
}
void NXMAccessManager::saveRefreshedTokens(const NexusOAuthTokens tokens)
diff --git a/src/src/nxmaccessmanager.h b/src/src/nxmaccessmanager.h
index c651bee..1668e90 100644
--- a/src/src/nxmaccessmanager.h
+++ b/src/src/nxmaccessmanager.h
@@ -215,6 +215,19 @@ public:
void refuseValidation();
+ /**
+ * @brief fetch the user's legacy personal API key via the OAuth bearer
+ * token and persist it as the legacy API key.
+ *
+ * Several plugins (Plugin Browser, older Python plugins) only consume the
+ * legacy `apiKey` field. The OAuth flow alone never populates it, so they
+ * fail with "User API Key is missing" even after a successful login.
+ * Calling Nexus' v1 `/users/validate.json` with the bearer token returns
+ * the user's personal API key in the `key` field; we store it so legacy
+ * code paths see a key.
+ **/
+ void fetchAndStoreLegacyApiKey();
+
signals:
/**