Skip to content

feat(extension): fill the 2FA code when the entry carries a TOTP secret - #328

Merged
vineethkrishnan merged 1 commit into
mainfrom
feat/extension-autofill-totp-code
Jul 29, 2026
Merged

feat(extension): fill the 2FA code when the entry carries a TOTP secret#328
vineethkrishnan merged 1 commit into
mainfrom
feat/extension-autofill-totp-code

Conversation

@vineethkrishnan

Copy link
Copy Markdown
Owner

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.ts for detection, the hasTotp flag on matches, and an origin-guarded generateTotp handler in the background. Only extension/entrypoints/content.ts changes.

How

Same-step forms. fillWithMatch now fills the 2FA box too when the picked entry has hasTotp. 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. maybeAutofillOtp fires from refreshMatches for static pages and off the existing mutation observer for SPA views that render the step later. It 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 digit boxes. The "one input per digit" widgets were previously filled with the whole code in box one, where maxlength=1 truncated it to a single character. fillOtpCode now 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 field isOtpInput accepts, because in those widgets only the first box usually carries a 2FA-ish name; the rest are digit-2, d3 and 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 generateTotp keeps its per-request host match check against the requesting tab.

Verification

  • npm run typecheck clean
  • npm test 65/65 passing
  • npm run build succeeds

Not 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 input events.

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.
@vineethkrishnan
vineethkrishnan merged commit c55b74a into main Jul 29, 2026
7 checks passed
@vineethkrishnan
vineethkrishnan deleted the feat/extension-autofill-totp-code branch July 29, 2026 06:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant