diff --git a/keepassxc-browser/content/keepassxc-browser.js b/keepassxc-browser/content/keepassxc-browser.js index f40c9b6..69c12dc 100755 --- a/keepassxc-browser/content/keepassxc-browser.js +++ b/keepassxc-browser/content/keepassxc-browser.js @@ -317,6 +317,7 @@ kpxcForm.saveForm = function(form, combination) { username: combination.username, password: combination.password, totp: combination.totp, + totpInputs: Array.from(form.elements).filter(e => e.nodeName === 'INPUT' && kpxcTOTPIcons.isValid(e)), passwordInputs: Array.from(form.elements).filter(e => e.nodeName === 'INPUT' && e.type === 'password') }); }; @@ -376,6 +377,29 @@ kpxcFields.getAllCombinations = async function(inputs) { combinations.push(combination); } + // Check for multiple segmented TOTP fields + if (combinations.length === 0) { + kpxcFields.getSegmentedTOTPFields(inputs, combinations); + } + + return combinations; +}; + +// Adds segmented TOTP fields to the combination if found +kpxcFields.getSegmentedTOTPFields = function(inputs, combinations) { + const form = inputs.length > 0 ? inputs[0].form : undefined; + if (acceptedOTPFields.some(f => form.className.includes(f) || form.id.includes(f) || form.name.includes(f))) { + const totpInputs = Array.from(form.elements).filter(e => e.nodeName === 'INPUT' && e.type !== 'password'); + if (totpInputs.length === 6) { + const combination = { + form: form, + totpInputs: totpInputs + }; + + combinations.push(combination); + } + } + return combinations; }; @@ -420,8 +444,8 @@ kpxcFields.getCombination = async function(field, givenType) { for (const combination of kpxc.combinations) { if (!givenType && Object.values(combination).find(c => c === field)) { return combination; - } else if (givenType) { - if (combination[givenType] === field) { + } else if (givenType && combination[givenType]) { + if (combination[givenType] === field || combination[givenType].includes(field)) { return combination; } } @@ -833,12 +857,12 @@ kpxc.fillFromTOTP = async function(target) { const el = target || document.activeElement; const credentialList = await kpxc.updateTOTPList(); - if (credentialList.length === 0) { + if (credentialList && credentialList.length === 0) { kpxcUI.createNotification('warning', tr('credentialsNoTOTPFound')); return; } - if (credentialList.length === 1) { + if (credentialList && credentialList.length === 1) { kpxc.fillTOTPFromUuid(el, credentialList[0].uuid); return; } @@ -865,18 +889,45 @@ kpxc.fillTOTPFromUuid = async function(el, uuid) { return; } - kpxc.setValue(el, totp); + kpxc.setTOTPValue(el, totp); } else if (user.stringFields && user.stringFields.length > 0) { const stringFields = user.stringFields; for (const s of stringFields) { const val = s['KPH: {TOTP}']; if (val) { - kpxc.setValue(el, val); + kpxc.setTOTPValue(el, val); } } } }; +// Set normal or segmented TOTP value +kpxc.setTOTPValue = function(elem, val) { + if (kpxc.combinations.length === 0) { + return; + } + + for (const comb of kpxc.combinations) { + if (comb.totpInputs && comb.totpInputs.length === 6) { + kpxc.fillSegmentedTotp(elem, val, comb.totpInputs); + return; + } + } + + kpxc.setValue(elem, val); +}; + +// Fill TOTP in parts +kpxc.fillSegmentedTotp = function(elem, val, totpInputs) { + if (!totpInputs.includes(elem)) { + return; + } + + for (let i = 0; i < 6; ++i) { + kpxc.setValue(totpInputs[i], val[i]); + } +}; + // Fill requested from username icon kpxc.fillFromUsernameIcon = async function(combination) { await kpxc.receiveCredentialsIfNecessary(); diff --git a/keepassxc-browser/content/totp-autocomplete.js b/keepassxc-browser/content/totp-autocomplete.js index 463cb52..0ed6de7 100644 --- a/keepassxc-browser/content/totp-autocomplete.js +++ b/keepassxc-browser/content/totp-autocomplete.js @@ -27,7 +27,8 @@ TOTPAutocomplete.prototype.itemEnter = async function(index, elements) { }; TOTPAutocomplete.prototype.fillTotp = async function(index, uuid) { - const combination = await kpxcFields.getCombination(this.input); + const combination = await kpxcFields.getCombination(this.input, 'totp') + || await kpxcFields.getCombination(this.input, 'totpInputs'); combination.loginId = index; kpxc.fillTOTPFromUuid(this.input, uuid); }; diff --git a/keepassxc-browser/content/totp-field.js b/keepassxc-browser/content/totp-field.js index b2968c6..0059d0c 100644 --- a/keepassxc-browser/content/totp-field.js +++ b/keepassxc-browser/content/totp-field.js @@ -13,6 +13,7 @@ const acceptedOTPFields = [ 'otp', 'token', 'twofa', + 'two-factor', 'twofactor' ];