From 5a0e211e1980b3bae4931909ab874c900e036e21 Mon Sep 17 00:00:00 2001 From: varjolintu Date: Tue, 23 Jan 2024 20:00:28 +0200 Subject: [PATCH 1/2] Add option to fill TOTP automatically --- keepassxc-browser/_locales/en/messages.json | 8 ++++++++ keepassxc-browser/background/page.js | 1 + keepassxc-browser/content/fill.js | 8 ++++++++ keepassxc-browser/content/keepassxc-browser.js | 6 +++++- keepassxc-browser/content/totp-field.js | 13 ++++++++++++- keepassxc-browser/options/options.html | 9 +++++++++ 6 files changed, 43 insertions(+), 2 deletions(-) diff --git a/keepassxc-browser/_locales/en/messages.json b/keepassxc-browser/_locales/en/messages.json index 3dc65dd..1e1ecc9 100644 --- a/keepassxc-browser/_locales/en/messages.json +++ b/keepassxc-browser/_locales/en/messages.json @@ -703,6 +703,10 @@ "message": "Automatically fill in single-credential entries.", "description": "Automatically fill-in single credential entry checkbox text." }, + "optionsCheckboxAutoFillSingleTotp": { + "message": "Automatically fill in single TOTP entries.", + "description": "Automatically fill-in single TOTP entries checkbox text." + }, "optionsCheckboxAutoCompleteUsernames": { "message": "Activate autocomplete for username fields.", "description": "Activate autocomplete for username fields checkbox text." @@ -847,6 +851,10 @@ "message": "Warning! Using auto-fill is not safe. Use at your own risk. KeePassXC-Browser automatically tries to detect login fields. However, they may be detected incorrectly, possibly filling sensitive data to unsafe input fields.", "description": "Auto-Fill Single Entry warning text." }, + "optionsAutoFillSingleTotpHelpText": { + "message": "Let KeePassXC-Browser automatically fill in TOTP fields if it receives only a single entry.", + "description": "Auto-Fill Single TOTP field option help text." + }, "optionsAutocompleteUsernamesHelpText": { "message": "Show a dropdown list containing available credentials for all username fields on a page.", "description": "Autocomplete Usernames option help text." diff --git a/keepassxc-browser/background/page.js b/keepassxc-browser/background/page.js index 0774047..d7c7495 100755 --- a/keepassxc-browser/background/page.js +++ b/keepassxc-browser/background/page.js @@ -7,6 +7,7 @@ const defaultSettings = { showGroupNameInAutocomplete: true, autoFillAndSend: false, autoFillSingleEntry: false, + autoFillSingleTotp: false, autoReconnect: false, autoRetrieveCredentials: true, autoSubmit: false, diff --git a/keepassxc-browser/content/fill.js b/keepassxc-browser/content/fill.js index 415d8a9..bb78cca 100644 --- a/keepassxc-browser/content/fill.js +++ b/keepassxc-browser/content/fill.js @@ -267,6 +267,14 @@ kpxcFill.fillInCredentials = async function(combination, predefinedUsername, uui kpxcFill.fillInStringFields(combination.fields, selectedCredentials.stringFields); } + // Fill TOTP + if (kpxc.settings.autoFillSingleTotp && kpxc.entryHasTotp(selectedCredentials)) { + const totpCombination = combination?.totp || kpxc.combinations?.find(c => c.totp); + if (totpCombination?.totp) { + kpxcFill.fillTOTPFromUuid(totpCombination.totp, selectedCredentials.uuid); + } + } + // Close autocomplete menu after fill kpxcUserAutocomplete.closeList(); diff --git a/keepassxc-browser/content/keepassxc-browser.js b/keepassxc-browser/content/keepassxc-browser.js index efde0a3..3c60bb2 100755 --- a/keepassxc-browser/content/keepassxc-browser.js +++ b/keepassxc-browser/content/keepassxc-browser.js @@ -126,6 +126,10 @@ kpxc.detectDatabaseChange = async function(response) { } }; +kpxc.entryHasTotp = function(entry) { + return entry.totp || (entry.stringFields && entry.stringFields.some(s => s['KPH: {TOTP}'])); +}; + // Get location URL by domain or full URL kpxc.getDocumentLocation = function() { return kpxc.settings.saveDomainOnly ? document.location.origin : document.location.href; @@ -776,7 +780,7 @@ kpxc.updateTOTPList = async function() { const password = credentials.password; // If no username is set, compare with a password - const credentialList = kpxc.credentials.filter(c => (c.totp || (c.stringFields && c.stringFields.some(s => s['KPH: {TOTP}']))) + const credentialList = kpxc.credentials.filter(c => kpxc.entryHasTotp(c) && (c.login === username || (!username && c.password === password))); // Filter TOTP Autocomplete Menu with matching 2FA credentials diff --git a/keepassxc-browser/content/totp-field.js b/keepassxc-browser/content/totp-field.js index 85e210c..b6a2d7c 100644 --- a/keepassxc-browser/content/totp-field.js +++ b/keepassxc-browser/content/totp-field.js @@ -108,7 +108,7 @@ class TOTPFieldIcon extends Icon { } } -TOTPFieldIcon.prototype.initField = function(field, segmented) { +TOTPFieldIcon.prototype.initField = async function(field, segmented) { // Observer the visibility if (this.observer) { this.observer.observe(field); @@ -116,6 +116,17 @@ TOTPFieldIcon.prototype.initField = function(field, segmented) { this.createIcon(field, segmented); this.inputField = field; + + // Fill TOTP automatically if option is enabled + if (kpxc.settings.autoFillSingleTotp) { + if (kpxc.credentials.length === 0) { + await kpxc.receiveCredentialsIfNecessary(); + } + + if (kpxc.credentials?.length === 1 && kpxc.entryHasTotp(kpxc.credentials[0])) { + kpxcFill.fillTOTPFromUuid(field, kpxc.credentials[0].uuid); + } + } }; TOTPFieldIcon.prototype.createIcon = function(field, segmented = false) { diff --git a/keepassxc-browser/options/options.html b/keepassxc-browser/options/options.html index 10f4ede..6134aa4 100644 --- a/keepassxc-browser/options/options.html +++ b/keepassxc-browser/options/options.html @@ -251,6 +251,15 @@ + +
+
+ + +
+
+
+
From b7739344d4415675557f4389697b7358dcdd0466 Mon Sep 17 00:00:00 2001 From: varjolintu Date: Sat, 27 Jan 2024 09:34:00 +0200 Subject: [PATCH 2/2] Code cleanup --- keepassxc-browser/content/totp-field.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/keepassxc-browser/content/totp-field.js b/keepassxc-browser/content/totp-field.js index b6a2d7c..9d5b9f5 100644 --- a/keepassxc-browser/content/totp-field.js +++ b/keepassxc-browser/content/totp-field.js @@ -108,16 +108,8 @@ class TOTPFieldIcon extends Icon { } } -TOTPFieldIcon.prototype.initField = async function(field, segmented) { - // Observer the visibility - if (this.observer) { - this.observer.observe(field); - } - - this.createIcon(field, segmented); - this.inputField = field; - - // Fill TOTP automatically if option is enabled +// Fill TOTP automatically if option is enabled +TOTPFieldIcon.prototype.autoFillSingleTotp = async function(field) { if (kpxc.settings.autoFillSingleTotp) { if (kpxc.credentials.length === 0) { await kpxc.receiveCredentialsIfNecessary(); @@ -129,6 +121,18 @@ TOTPFieldIcon.prototype.initField = async function(field, segmented) { } }; +TOTPFieldIcon.prototype.initField = async function(field, segmented) { + // Observer the visibility + if (this.observer) { + this.observer.observe(field); + } + + this.createIcon(field, segmented); + this.inputField = field; + + await this.autoFillSingleTotp(field); +}; + TOTPFieldIcon.prototype.createIcon = function(field, segmented = false) { const className = (isFirefox() ? 'moz' : 'default');