feat(extension): fill the 2FA code when the entry carries a TOTP secret - #328
Merged
Merged
Conversation
Autofill stopped at the username and password: the one-time-code field was only ever filled by clicking its emblem and then a picker row, even when the login being filled already carried a TOTP secret. Fill it as part of the fill instead. Everything needed was already in place (the one-time-code field detector, the hasTotp flag on matches, and an origin-guarded generateTotp handler in the background); none of it was wired into the fill path. A single-step form, with the 2FA box next to the password, is now filled by the credential fill itself on any explicit picker click. The far more common shape, where the code field only appears after the password is submitted, is covered by a separate pass that runs on load and off the existing mutation observer for SPA views. That one is gated by the existing autofill setting and, like autofill-on-load, only acts when the page is unambiguous: exactly one matching login carrying a secret, an untouched field, an unlocked vault, once per page. Split "one box per digit" widgets were previously filled with the whole code in box one, where maxlength=1 truncated it to a single character. The digits are now spread across the run of single-character inputs starting at the anchor. The group is that run rather than every field the detector accepts, because only the first box usually carries a 2FA-ish name; the rest are digit-2, d3 and so on, and matching on names alone would fill box one and skip the others. The manual picker click goes through the same helper, so that path is fixed too. No new setting, and no change to the security boundary: the TOTP secret still never leaves the background, only the short-lived code crosses into the page, and generateTotp keeps its per-request host check against the requesting tab.
This was referenced Jul 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Autofill stopped at the username and password. The one-time-code field was only ever filled by clicking its emblem and then a picker row, even when the login being filled already carried a TOTP secret. This wires the code into the fill itself.
Everything needed already existed and was simply never connected to the fill path:
utils/otp-field.tsfor detection, thehasTotpflag on matches, and an origin-guardedgenerateTotphandler in the background. Onlyextension/entrypoints/content.tschanges.How
Same-step forms.
fillWithMatchnow fills the 2FA box too when the picked entry hashasTotp. It looks for a code field inside the form first, then falls back to the document, and only writes into an untouched field. This runs on any explicit picker click, no setting required.Second-step 2FA pages. The far more common shape, where the code field only appears after the password is submitted.
maybeAutofillOtpfires fromrefreshMatchesfor static pages and off the existing mutation observer for SPA views that render the step later. It is gated by the existingautofillsetting and, like autofill-on-load, only acts when the page is unambiguous: exactly one matching login carrying a secret, an untouched field, an unlocked vault, once per page.Split digit boxes. The "one input per digit" widgets were previously filled with the whole code in box one, where
maxlength=1truncated it to a single character.fillOtpCodenow spreads the digits across the run of single-character inputs starting at the anchor and focuses the last one. The group is that run rather than every fieldisOtpInputaccepts, because in those widgets only the first box usually carries a 2FA-ish name; the rest aredigit-2,d3and so on, so matching on names alone would fill box one and skip the others. The manual picker click goes through the same helper, so that path is fixed too.Security
No new setting and no change to the boundary. The TOTP secret still never leaves the background, only the short-lived code crosses into the page, and
generateTotpkeeps its per-request host match check against the requesting tab.Verification
npm run typecheckcleannpm test65/65 passingnpm run buildsucceedsNot exercised against a live 2FA page in a browser. The split-box distribution in particular is worth a manual check on a real site, since some widgets move focus themselves in response to
inputevents.