Skip to content

Detect and fill login fields inside open shadow roots - #348

Open
blacksun80 wants to merge 2 commits into
kee-org:masterfrom
blacksun80:feature/shadow-dom-support
Open

blacksun80 wants to merge 2 commits into
kee-org:masterfrom
blacksun80:feature/shadow-dom-support

Conversation

@blacksun80

Copy link
Copy Markdown

Problem

Kee finds fields via document.forms and getElementsByTagName("input"), so it never sees inputs that live inside a shadow root. More and more login pages are built from web components.

A concrete example is the new GMX login at https://auth.gmx.net/login, a two-step form:

  1. The first step only shows the username field (<input type="email" autocomplete="username">, no name). Kee ignored this form because it has no password field, unless the user whitelisted the field manually.
  2. After clicking "Next" the password field appears. It sits inside the open shadow root of a <lux-password> custom element, which is placed inside the login <form>. Kee never found it.

Similar reports on the forum: Chase, auth.max.com, web.de/gmx.de.

Solution

Commit 1: Find and fill fields in open shadow roots

  • New helper src/page/shadowDom.ts finds open shadow roots (including nested ones) and the inputs inside them.
  • FormUtils.getFormFields also returns the inputs from shadow roots whose outermost host is inside the form. Username and password are then still detected and filled as one form.
  • Shadow root inputs whose host is not inside any form go into the existing pseudo form for orphaned fields.
  • The MutationObserver in page.ts also observes open shadow roots (both those present at startup and those added later), so fields rendered into a shadow root later on still trigger a rescan.

Commit 2: Treat autocomplete="username" fields as login fields

A text field with autocomplete="username" is now preferred as the username field and makes a form interesting even without a password field, as a whitelisted field would. This covers the first step of multi-step logins without per-site configuration. It is a separate commit because it is a (small) behaviour change beyond shadow DOM support; it can be dropped on its own if you'd rather not include it.

Limitations

  • Closed shadow roots are not accessible to extensions and remain unsupported.
  • A shadow root attached to an element that is already in the page doesn't cause a DOM mutation, so it is only picked up on the next regular scan.
  • Every scan now walks the page once to find shadow roots (using a native TreeWalker).
  • Shadow root inputs are appended after the form's own elements, which matters only for the "field before the password" username fallback.
  • Submit buttons inside shadow roots are not searched for.

Testing

  • New Vitest tests (jsdom) in src/tests/shadowDom.test.ts and src/tests/formsUtils.test.ts: open, nested and closed shadow roots, hosts inside and outside forms, a <lux-password>-like form, and autocomplete="username". The existing ConfigManager.test.ts failures are unrelated and also happen on master.
  • tsc --noEmit and eslint report no new problems.
  • Manually tested with the Chrome MV3 build (npm run build-chrome:beta) loaded unpacked in Edge with KeePassRPC on https://auth.gmx.net/login: the username is filled on step 1 without any whitelist entry and the password is filled on step 2.

🤖 Generated with Claude Code

blacksun80 and others added 2 commits October 8, 2026 19:56
Inputs inside a shadow root are not part of document.forms and are not
returned by getElementsByTagName on the document, so login forms built
with web components (e.g. the password field of <lux-password> on
auth.gmx.net) were never detected.

Fields from open shadow roots (including nested ones) now belong to the
form containing their outermost shadow host, so username and password
are still filled together. Fields whose host is outside of any form go
into the pseudo form used for orphaned fields. The DOM mutation
observer also observes open shadow roots so fields added to them later
trigger a rescan. Closed shadow roots remain inaccessible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Multi-step login pages (e.g. auth.gmx.net) often show only the username
field on the first step, with no password field in the form. Such forms
were ignored unless a field had been whitelisted manually.

A text field with autocomplete="username" is now preferred as the
username field and makes a form interesting even without a password
field, as a whitelisted field would.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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