diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-05-02 00:18:53 -0500 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-05-02 00:18:53 -0500 |
| commit | 44515159af50756ff26a4996ca1526f7e51e9e98 (patch) | |
| tree | 8e7c012235bf9456011f13f24bf5bde9222fbe51 /src | |
| parent | 73fc2ae731a4ef972c16efb31a30da9e9a8645c6 (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>
Diffstat (limited to 'src')
| -rw-r--r-- | src/src/nxmaccessmanager.cpp | 58 | ||||
| -rw-r--r-- | src/src/nxmaccessmanager.h | 13 |
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: /** |
