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..9d5b9f5 100644 --- a/keepassxc-browser/content/totp-field.js +++ b/keepassxc-browser/content/totp-field.js @@ -108,7 +108,20 @@ class TOTPFieldIcon extends Icon { } } -TOTPFieldIcon.prototype.initField = function(field, segmented) { +// 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(); + } + + if (kpxc.credentials?.length === 1 && kpxc.entryHasTotp(kpxc.credentials[0])) { + kpxcFill.fillTOTPFromUuid(field, kpxc.credentials[0].uuid); + } + } +}; + +TOTPFieldIcon.prototype.initField = async function(field, segmented) { // Observer the visibility if (this.observer) { this.observer.observe(field); @@ -116,6 +129,8 @@ TOTPFieldIcon.prototype.initField = function(field, segmented) { this.createIcon(field, segmented); this.inputField = field; + + await this.autoFillSingleTotp(field); }; 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 @@ + +