diff --git a/docs/topics/DatabaseOperations.adoc b/docs/topics/DatabaseOperations.adoc
index b3098fc9a..f29e4b3d6 100644
--- a/docs/topics/DatabaseOperations.adoc
+++ b/docs/topics/DatabaseOperations.adoc
@@ -61,7 +61,7 @@ image::database_view.png[]
=== Quick Unlock
On Windows and macOS, subject to hardware availability, your credentials can be securely stored to enable subsequent unlocking of your database through biometric authentication. This is enabled by default on Windows using _Windows Hello_ and on macOS using _Touch ID or Apple Watch_ services. You can disable this feature in the Application Settings under the Security section.
-NOTE: On Windows you will be prompted to authenticate to Windows Hello on the initial database unlock. This is required to access the hardware certificate store that encrypts your credentials.
+NOTE: On Windows, you will be prompted to authenticate to Windows Hello after unlocking your database with full credentials. This is required to setup Quick Unlock. If you cancel this prompt then Quick Unlock will not be enabled and your database will continue to unlock.
.Windows Hello example
image::quick_unlock_windows_hello.png[]
diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts
index a01154a48..599c8a696 100644
--- a/share/translations/keepassxc_en.ts
+++ b/share/translations/keepassxc_en.ts
@@ -1613,6 +1613,10 @@ If you do not have a key file, please leave the field empty.
+
+
+
+
DatabaseSettingWidgetMetaData
diff --git a/src/gui/ApplicationSettingsWidget.cpp b/src/gui/ApplicationSettingsWidget.cpp
index b48aaf0a1..56d60ea08 100644
--- a/src/gui/ApplicationSettingsWidget.cpp
+++ b/src/gui/ApplicationSettingsWidget.cpp
@@ -164,15 +164,6 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent)
m_generalUi->faviconTimeoutLabel->setVisible(false);
m_generalUi->faviconTimeoutSpinBox->setVisible(false);
#endif
-
- bool showQuickUnlock = false;
-#if defined(Q_OS_MACOS)
- showQuickUnlock = TouchID::getInstance().isAvailable();
-#elif defined(Q_CC_MSVC)
- showQuickUnlock = getWindowsHello()->isAvailable();
- connect(getWindowsHello(), &WindowsHello::availableChanged, m_secUi->quickUnlockCheckBox, &QCheckBox::setVisible);
-#endif
- m_secUi->quickUnlockCheckBox->setVisible(showQuickUnlock);
}
ApplicationSettingsWidget::~ApplicationSettingsWidget()
@@ -325,6 +316,14 @@ void ApplicationSettingsWidget::loadSettings()
m_secUi->EnableCopyOnDoubleClickCheckBox->setChecked(
config()->get(Config::Security_EnableCopyOnDoubleClick).toBool());
+ bool quickUnlockAvailable = false;
+#if defined(Q_OS_MACOS)
+ quickUnlockAvailable = TouchID::getInstance().isAvailable();
+#elif defined(Q_CC_MSVC)
+ quickUnlockAvailable = getWindowsHello()->isAvailable();
+ connect(getWindowsHello(), &WindowsHello::availableChanged, m_secUi->quickUnlockCheckBox, &QCheckBox::setEnabled);
+#endif
+ m_secUi->quickUnlockCheckBox->setEnabled(quickUnlockAvailable);
m_secUi->quickUnlockCheckBox->setChecked(config()->get(Config::Security_QuickUnlock).toBool());
for (const ExtraPage& page : asConst(m_extraPages)) {
@@ -437,7 +436,9 @@ void ApplicationSettingsWidget::saveSettings()
m_secUi->NoConfirmMoveEntryToRecycleBinCheckBox->isChecked());
config()->set(Config::Security_EnableCopyOnDoubleClick, m_secUi->EnableCopyOnDoubleClickCheckBox->isChecked());
- config()->set(Config::Security_QuickUnlock, m_secUi->quickUnlockCheckBox->isChecked());
+ if (m_secUi->quickUnlockCheckBox->isEnabled()) {
+ config()->set(Config::Security_QuickUnlock, m_secUi->quickUnlockCheckBox->isChecked());
+ }
// Security: clear storage if related settings are disabled
if (!config()->get(Config::RememberLastDatabases).toBool()) {
diff --git a/src/gui/DatabaseOpenDialog.cpp b/src/gui/DatabaseOpenDialog.cpp
index 366f7a9cc..25c0fd25e 100644
--- a/src/gui/DatabaseOpenDialog.cpp
+++ b/src/gui/DatabaseOpenDialog.cpp
@@ -84,6 +84,16 @@ DatabaseOpenDialog::DatabaseOpenDialog(QWidget* parent)
connect(shortcut, &QShortcut::activated, this, [this]() { selectTabOffset(1); });
}
+void DatabaseOpenDialog::showEvent(QShowEvent* event)
+{
+ QDialog::showEvent(event);
+ QTimer::singleShot(100, this, [=] {
+ if (m_view->isOnQuickUnlockScreen() && !m_view->unlockingDatabase()) {
+ m_view->triggerQuickUnlock();
+ }
+ });
+}
+
void DatabaseOpenDialog::selectTabOffset(int offset)
{
if (offset == 0 || m_tabBar->count() <= 1) {
diff --git a/src/gui/DatabaseOpenDialog.h b/src/gui/DatabaseOpenDialog.h
index 5fcee76a9..b1a59b59a 100644
--- a/src/gui/DatabaseOpenDialog.h
+++ b/src/gui/DatabaseOpenDialog.h
@@ -58,6 +58,9 @@ public slots:
void complete(bool accepted);
void tabChanged(int index);
+protected:
+ void showEvent(QShowEvent* event) override;
+
private:
void selectTabOffset(int offset);
diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp
index a98d0dba8..f3adeee29 100644
--- a/src/gui/DatabaseOpenWidget.cpp
+++ b/src/gui/DatabaseOpenWidget.cpp
@@ -139,6 +139,7 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent)
// QuickUnlock actions
connect(m_ui->quickUnlockButton, &QPushButton::pressed, this, [this] { openDatabase(); });
connect(m_ui->resetQuickUnlockButton, &QPushButton::pressed, this, [this] { resetQuickUnlock(); });
+ m_ui->resetQuickUnlockButton->setShortcut(Qt::Key_Escape);
}
DatabaseOpenWidget::~DatabaseOpenWidget()
@@ -286,7 +287,11 @@ void DatabaseOpenWidget::openDatabase()
auto keyData = databaseKey->serialize();
#if defined(Q_CC_MSVC)
// Store the password using Windows Hello
- getWindowsHello()->storeKey(m_filename, keyData);
+ if (!getWindowsHello()->storeKey(m_filename, keyData)) {
+ getMainWindow()->displayTabMessage(
+ tr("Windows Hello setup was canceled or failed. Quick unlock has not been enabled."),
+ MessageWidget::MessageType::Warning);
+ }
#elif defined(Q_OS_MACOS)
// Store the password using TouchID
TouchID::getInstance().storeKey(m_filename, keyData);
@@ -536,6 +541,13 @@ bool DatabaseOpenWidget::isOnQuickUnlockScreen()
return m_ui->centralStack->currentIndex() == 1;
}
+void DatabaseOpenWidget::triggerQuickUnlock()
+{
+ if (isOnQuickUnlockScreen()) {
+ m_ui->quickUnlockButton->click();
+ }
+}
+
/**
* Reset installed quick unlock secrets.
*
diff --git a/src/gui/DatabaseOpenWidget.h b/src/gui/DatabaseOpenWidget.h
index a55adf196..12d2efd11 100644
--- a/src/gui/DatabaseOpenWidget.h
+++ b/src/gui/DatabaseOpenWidget.h
@@ -45,9 +45,13 @@ public:
void clearForms();
void enterKey(const QString& pw, const QString& keyFile);
QSharedPointer database();
- void resetQuickUnlock();
bool unlockingDatabase();
+ // Quick Unlock helper functions
+ bool isOnQuickUnlockScreen();
+ void triggerQuickUnlock();
+ void resetQuickUnlock();
+
signals:
void dialogFinished(bool accepted);
@@ -56,8 +60,6 @@ protected:
void hideEvent(QHideEvent* event) override;
QSharedPointer buildDatabaseKey();
void setUserInteractionLock(bool state);
- // Quick Unlock helper functions
- bool isOnQuickUnlockScreen();
const QScopedPointer m_ui;
QSharedPointer m_db;
diff --git a/tests/gui/TestGuiBrowser.cpp b/tests/gui/TestGuiBrowser.cpp
index 075d49dc9..15f19d47d 100644
--- a/tests/gui/TestGuiBrowser.cpp
+++ b/tests/gui/TestGuiBrowser.cpp
@@ -70,6 +70,8 @@ void TestGuiBrowser::initTestCase()
config()->set(Config::GUI_AdvancedSettings, false);
// Disable the update check first time alert
config()->set(Config::UpdateCheckMessageShown, true);
+ // Disable quick unlock
+ config()->set(Config::Security_QuickUnlock, false);
m_mainWindow.reset(new MainWindow());
m_tabWidget = m_mainWindow->findChild("tabWidget");
diff --git a/tests/gui/TestGuiFdoSecrets.cpp b/tests/gui/TestGuiFdoSecrets.cpp
index 0f9d62040..7a83a05f9 100644
--- a/tests/gui/TestGuiFdoSecrets.cpp
+++ b/tests/gui/TestGuiFdoSecrets.cpp
@@ -142,6 +142,8 @@ void TestGuiFdoSecrets::initTestCase()
config()->set(Config::AutoSaveOnExit, false);
config()->set(Config::GUI_ShowTrayIcon, true);
config()->set(Config::UpdateCheckMessageShown, true);
+ // Disable quick unlock
+ config()->set(Config::Security_QuickUnlock, false);
// Disable secret service integration (activate within individual tests to test the plugin)
FdoSecrets::settings()->setEnabled(false);
// activate within individual tests