qml: PasswordDialog: show error on invalid password
What changed, and why it matters
This commit fixes a confusing user-interface behavior in Electrum's mobile/QML app. Previously, when a user typed the wrong password, the password dialog simply closed without explanation, which could make them think an action had happened. Now the dialog stays open and shows an 'Invalid Password' message. It is a straightforward usability improvement, not a security vulnerability fix.
No security action required. Treat as a normal usability/UX fix. Reviewers may optionally verify that `passwordEntered` handlers always close the dialog on success to avoid lingering password fields.
Security signals we found
No authentication bypass: password verification still occurs before proceeding
No secret exposure: password handling remains within existing QML/Python boundary
UI-only change: dialog lifecycle and error presentation, not cryptographic logic
No references to CVEs, advisories, or security disclosures in commit or diff
Evidence from the diff
The change refactors the QML PasswordDialog to emit a passwordEntered signal instead of storing the password in a property and relying on the generic accepted signal. Callers now validate the password themselves and can set errorMessage to keep the dialog open on failure. The previous behavior closed the dialog on any password entry and called authCancel() for invalid passwords, which was poor UX but did not bypass authentication or expose secrets.
Changed components
electrum/gui/qml/components/PasswordDialog.qmlelectrum/gui/qml/components/WalletDetails.qmlelectrum/gui/qml/components/main.qmlInspect captured patch +35 / −15
diff --git a/electrum/gui/qml/components/PasswordDialog.qml b/electrum/gui/qml/components/PasswordDialog.qml
index 71a43d9..550f00b 100644
--- a/electrum/gui/qml/components/PasswordDialog.qml
+++ b/electrum/gui/qml/components/PasswordDialog.qml
@@ -14,8 +14,10 @@ ElDialog {
iconSource: Qt.resolvedUrl('../../icons/lock.png')
property bool confirmPassword: false
- property string password
property string infotext
+ property string errorMessage
+
+ signal passwordEntered(string password)
anchors.centerIn: parent
width: parent.width * 4/5
@@ -84,6 +86,16 @@ ElDialog {
password: pw_1.text
}
}
+
+ Label {
+ Layout.maximumWidth: parent.width
+ Layout.alignment: Qt.AlignHCenter
+ text: errorMessage
+ wrapMode: Text.Wrap
+ visible: errorMessage
+ color: constants.colorError
+ font.pixelSize: constants.fontSizeLarge
+ }
}
FlatButton {
@@ -92,10 +104,13 @@ ElDialog {
icon.source: '../../icons/confirmed.png'
enabled: confirmPassword ? pw_1.text.length >= 6 && pw_1.text == pw_2.text : true
onClicked: {
- password = pw_1.text
- passworddialog.doAccept()
+ passwordEntered(pw_1.text)
}
}
}
+ function clearPassword() {
+ pw_1.text = ""
+ pw_2.text = ""
+ }
}
diff --git a/electrum/gui/qml/components/WalletDetails.qml b/electrum/gui/qml/components/WalletDetails.qml
index a86cce3..8522c42 100644
--- a/electrum/gui/qml/components/WalletDetails.qml
+++ b/electrum/gui/qml/components/WalletDetails.qml
@@ -473,12 +473,13 @@ Pane {
title: qsTr('Enter new password'),
infotext: qsTr('If you forget your password, you\'ll need to restore from seed. Please make sure you have your seed stored safely')
})
- dialog.accepted.connect(function() {
- var success = Daemon.setPassword(dialog.password)
+ dialog.passwordEntered.connect(function(password) {
+ dialog.close()
+ var success = Daemon.setPassword(password)
if (success && Biometrics.isEnabled) {
if (Biometrics.isAvailable) {
// also update the biometric authentication
- Biometrics.enable(dialog.password)
+ Biometrics.enable(password)
} else {
// disable biometric authentication as it is not available
Biometrics.disable()
@@ -538,23 +539,25 @@ Pane {
? "\n\n" + qsTr('The new password needs to match the password of any other existing wallet.')
: "")
})
- dialog.accepted.connect(function() {
+ dialog.passwordEntered.connect(function(password) {
if (Config.walletShouldUseSinglePassword // android
&& Daemon.availableWallets.rowCount() > 1 // has more than one wallet
- && Daemon.numWalletsWithPassword(dialog.password) < 1 // no other wallet uses this new password
+ && Daemon.numWalletsWithPassword(password) < 1 // no other wallet uses this new password
) {
- var success = false
- var error_msg = [
+ dialog.errorMessage = [
qsTr('You need to use the password of any other existing wallet.'),
qsTr('Using different wallet passwords is not supported.'),
].join("\n")
+ dialog.clearPassword()
+ return
} else {
- var success = Daemon.currentWallet.setPassword(dialog.password)
+ var success = Daemon.currentWallet.setPassword(password)
if (success && Config.walletShouldUseSinglePassword) {
- Daemon.singlePassword = dialog.password
+ Daemon.singlePassword = password
}
var error_msg = qsTr('Password change failed')
}
+ dialog.close()
if (success && Biometrics.isEnabled) {
// unlikely to happen as this means the user somehow moved from
// a unified password to differing passwords
diff --git a/electrum/gui/qml/components/main.qml b/electrum/gui/qml/components/main.qml
index 5a5fb58..4202130 100644
--- a/electrum/gui/qml/components/main.qml
+++ b/electrum/gui/qml/components/main.qml
@@ -852,11 +852,13 @@ ApplicationWindow
// 'payment_auth' should have been converted to 'wallet' at this point
if (method === 'wallet' || method === 'wallet_password_only') {
var dialog = app.passwordDialog.createObject(app, authMessage ? {'title': authMessage} : {})
- dialog.accepted.connect(function() {
- if (Daemon.currentWallet.verifyPassword(dialog.password)) {
+ dialog.passwordEntered.connect(function(password) {
+ if (Daemon.currentWallet.verifyPassword(password)) {
+ dialog.close()
qtobject.authProceed()
} else {
- qtobject.authCancel()
+ dialog.clearPassword()
+ dialog.errorMessage = qsTr("Invalid Password")
}
})
dialog.rejected.connect(function() {
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.