From a8e9e800b3ff7e896ab7dc2ce27bc9328e63b958 Mon Sep 17 00:00:00 2001
From: Tom Piccirello <8296030+Piccirello@users.noreply.github.com>
Date: Wed, 22 Oct 2025 01:05:51 -0700
Subject: [PATCH] Support deleting API key
PR #23388.
---
WebAPI_Changelog.md | 2 +
src/gui/optionsdialog.cpp | 65 +++++++++++++------
src/gui/optionsdialog.h | 6 +-
src/gui/optionsdialog.ui | 29 +++++++++
src/webui/api/appcontroller.cpp | 7 ++
src/webui/api/appcontroller.h | 1 +
src/webui/webapplication.h | 3 +-
...rmRotateAPIKey.html => confirmAPIKey.html} | 25 ++++---
src/webui/www/private/views/preferences.html | 55 ++++++++++++++--
src/webui/www/webui.qrc | 2 +-
10 files changed, 158 insertions(+), 37 deletions(-)
rename src/webui/www/private/views/{confirmRotateAPIKey.html => confirmAPIKey.html} (65%)
diff --git a/WebAPI_Changelog.md b/WebAPI_Changelog.md
index 0ed5ba7a4..3b88c8d13 100644
--- a/WebAPI_Changelog.md
+++ b/WebAPI_Changelog.md
@@ -3,6 +3,8 @@
## 2.14.1
* [#23212](https://github.com/qbittorrent/qBittorrent/pull/23212)
* Add `app/rotateAPIKey` endpoint for generating, and rotating, the WebAPI API key
+* [#23388](https://github.com/qbittorrent/qBittorrent/pull/23388)
+ * Add `app/deleteAPIKey` endpoint for deleting the existing WebAPI API key
## 2.14.0
* [#23202](https://github.com/qbittorrent/qBittorrent/pull/23202)
diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp
index 5ea18fe06..b050e8221 100644
--- a/src/gui/optionsdialog.cpp
+++ b/src/gui/optionsdialog.cpp
@@ -1356,21 +1356,10 @@ void OptionsDialog::loadWebUITabOptions()
// API Key
if (const QString apiKey = pref->getWebUIApiKey(); Utils::APIKey::isValid(apiKey))
- {
m_currentAPIKey = apiKey;
- m_ui->textWebUIAPIKey->setText(maskAPIKey(m_currentAPIKey));
- m_ui->textWebUIAPIKey->setEnabled(true);
- m_ui->btnWebUIAPIKeyCopy->setEnabled(true);
- m_ui->btnWebUIAPIKeyRotate->setToolTip(tr("Rotate API key"));
- }
else
- {
m_currentAPIKey.clear();
- m_ui->textWebUIAPIKey->clear();
- m_ui->textWebUIAPIKey->setEnabled(false);
- m_ui->btnWebUIAPIKeyCopy->setEnabled(false);
- m_ui->btnWebUIAPIKeyRotate->setToolTip(tr("Generate API key"));
- }
+ setupWebUIAPIKey();
m_ui->checkBypassLocalAuth->setChecked(!pref->isWebUILocalAuthEnabled());
m_ui->checkBypassAuthSubnetWhitelist->setChecked(pref->isWebUIAuthSubnetWhitelistEnabled());
@@ -1412,8 +1401,9 @@ void OptionsDialog::loadWebUITabOptions()
connect(m_ui->textWebUIUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
connect(m_ui->textWebUIPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
- connect(m_ui->btnWebUIAPIKeyCopy, &QPushButton::clicked, this, &ThisType::onBtnWebUIAPIKeyCopy);
- connect(m_ui->btnWebUIAPIKeyRotate, &QPushButton::clicked, this, &ThisType::onBtnWebUIAPIKeyRotate);
+ connect(m_ui->btnWebUIAPIKeyCopy, &QPushButton::clicked, this, &ThisType::onBtnWebUIAPIKeyCopyClicked);
+ connect(m_ui->btnWebUIAPIKeyRotate, &QPushButton::clicked, this, &ThisType::onBtnWebUIAPIKeyRotateClicked);
+ connect(m_ui->btnWebUIAPIKeyDelete, &QPushButton::clicked, this, &ThisType::onBtnWebUIAPIKeyDeleteClicked);
connect(m_ui->checkBypassLocalAuth, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkBypassAuthSubnetWhitelist, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
@@ -1490,13 +1480,13 @@ void OptionsDialog::saveWebUITabOptions() const
pref->setDynDNSPassword(m_ui->DNSPasswordTxt->text());
}
-void OptionsDialog::onBtnWebUIAPIKeyCopy()
+void OptionsDialog::onBtnWebUIAPIKeyCopyClicked()
{
if (!m_currentAPIKey.isEmpty())
QApplication::clipboard()->setText(m_currentAPIKey);
}
-void OptionsDialog::onBtnWebUIAPIKeyRotate()
+void OptionsDialog::onBtnWebUIAPIKeyRotateClicked()
{
const QString title = m_currentAPIKey.isEmpty()
? tr("Generate API key")
@@ -1511,16 +1501,51 @@ void OptionsDialog::onBtnWebUIAPIKeyRotate()
if (button == QMessageBox::Yes)
{
m_currentAPIKey = Utils::APIKey::generate();
- m_ui->textWebUIAPIKey->setText(maskAPIKey(m_currentAPIKey));
- m_ui->textWebUIAPIKey->setEnabled(true);
- m_ui->btnWebUIAPIKeyCopy->setEnabled(true);
- m_ui->btnWebUIAPIKeyRotate->setToolTip(tr("Rotate API key"));
+ setupWebUIAPIKey();
auto *preferences = Preferences::instance();
preferences->setWebUIApiKey(m_currentAPIKey);
preferences->apply();
}
}
+
+void OptionsDialog::onBtnWebUIAPIKeyDeleteClicked()
+{
+ const QString title = tr("Delete API key");
+ const QString message = tr("Delete this API key? The current key will immediately stop working.");
+ const QMessageBox::StandardButton button = QMessageBox::question(
+ this, title, message, (QMessageBox::Yes | QMessageBox::No), QMessageBox::No);
+
+ if (button == QMessageBox::Yes)
+ {
+ m_currentAPIKey.clear();
+ setupWebUIAPIKey();
+
+ auto *preferences = Preferences::instance();
+ preferences->setWebUIApiKey(m_currentAPIKey);
+ preferences->apply();
+ }
+}
+
+void OptionsDialog::setupWebUIAPIKey()
+{
+ if (Utils::APIKey::isValid(m_currentAPIKey))
+ {
+ m_ui->textWebUIAPIKey->setText(maskAPIKey(m_currentAPIKey));
+ m_ui->textWebUIAPIKey->setEnabled(true);
+ m_ui->btnWebUIAPIKeyCopy->setEnabled(true);
+ m_ui->btnWebUIAPIKeyRotate->setToolTip(tr("Rotate API key"));
+ m_ui->btnWebUIAPIKeyDelete->setEnabled(true);
+ }
+ else
+ {
+ m_ui->textWebUIAPIKey->clear();
+ m_ui->textWebUIAPIKey->setEnabled(false);
+ m_ui->btnWebUIAPIKeyCopy->setEnabled(false);
+ m_ui->btnWebUIAPIKeyRotate->setToolTip(tr("Generate API key"));
+ m_ui->btnWebUIAPIKeyDelete->setEnabled(false);
+ }
+}
#endif // DISABLE_WEBUI
void OptionsDialog::initializeLanguageCombo()
diff --git a/src/gui/optionsdialog.h b/src/gui/optionsdialog.h
index c63d8e6c7..2a6266198 100644
--- a/src/gui/optionsdialog.h
+++ b/src/gui/optionsdialog.h
@@ -110,8 +110,10 @@ private slots:
void webUIHttpsCertChanged(const Path &path);
void webUIHttpsKeyChanged(const Path &path);
void on_registerDNSBtn_clicked();
- void onBtnWebUIAPIKeyCopy();
- void onBtnWebUIAPIKeyRotate();
+ void onBtnWebUIAPIKeyCopyClicked();
+ void onBtnWebUIAPIKeyRotateClicked();
+ void onBtnWebUIAPIKeyDeleteClicked();
+ void setupWebUIAPIKey();
#endif
private:
diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui
index 6a138971b..0e04f372d 100644
--- a/src/gui/optionsdialog.ui
+++ b/src/gui/optionsdialog.ui
@@ -3861,6 +3861,35 @@ Specify an IPv4 or IPv6 address. You can specify "0.0.0.0" for any IPv
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 32
+
+
+
+ Delete API key
+
+
+
+
+
+
+ :/icons/list-remove.svg:/icons/list-remove.svg
+
+
+ false
+
+
+
diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp
index 014c9bfde..3da495fdc 100644
--- a/src/webui/api/appcontroller.cpp
+++ b/src/webui/api/appcontroller.cpp
@@ -1326,6 +1326,13 @@ void AppController::rotateAPIKeyAction()
setResult(QJsonObject {{u"apiKey"_s, key}});
}
+void AppController::deleteAPIKeyAction()
+{
+ auto *preferences = Preferences::instance();
+ preferences->setWebUIApiKey({});
+ preferences->apply();
+}
+
void AppController::networkInterfaceListAction()
{
QJsonArray ifaceList;
diff --git a/src/webui/api/appcontroller.h b/src/webui/api/appcontroller.h
index 8426a474f..ecc5c99ed 100644
--- a/src/webui/api/appcontroller.h
+++ b/src/webui/api/appcontroller.h
@@ -53,6 +53,7 @@ private slots:
void cookiesAction();
void setCookiesAction();
void rotateAPIKeyAction();
+ void deleteAPIKeyAction();
void networkInterfaceListAction();
void networkInterfaceAddressListAction();
diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h
index 16554342a..032491a18 100644
--- a/src/webui/webapplication.h
+++ b/src/webui/webapplication.h
@@ -53,7 +53,7 @@
#include "base/utils/version.h"
#include "api/isessionmanager.h"
-inline const Utils::Version<3, 2> API_VERSION {2, 14, 0};
+inline const Utils::Version<3, 2> API_VERSION {2, 14, 1};
class APIController;
class AuthController;
@@ -150,6 +150,7 @@ private:
const QHash, QString> m_allowedMethod =
{
// <, HTTP method>
+ {{u"app"_s, u"deleteAPIKey"_s}, Http::METHOD_POST},
{{u"app"_s, u"rotateAPIKey"_s}, Http::METHOD_POST},
{{u"app"_s, u"sendTestEmail"_s}, Http::METHOD_POST},
{{u"app"_s, u"setCookies"_s}, Http::METHOD_POST},
diff --git a/src/webui/www/private/views/confirmRotateAPIKey.html b/src/webui/www/private/views/confirmAPIKey.html
similarity index 65%
rename from src/webui/www/private/views/confirmRotateAPIKey.html
rename to src/webui/www/private/views/confirmAPIKey.html
index 44df72593..73bde96e6 100644
--- a/src/webui/www/private/views/confirmRotateAPIKey.html
+++ b/src/webui/www/private/views/confirmAPIKey.html
@@ -1,24 +1,24 @@
-