fix: accept values written into the login fields by a password manager #2

Merged
mael merged 1 commit from fix/password-manager-autofill into main 2026-09-17 18:05:04 +02:00
Owner

Extensions fill a field by assigning element.value and then dispatching
input/change. React installs its own value descriptor on the
element, so that assignment also updates React's internal value tracker;
when the event arrives React compares old and new, sees no difference,
and suppresses the synthetic onChange. react-hook-form never learns
the field was filled, formState.isValid stays false, the submit button
stays disabled, and handleSubmit would send an empty value.

The user sees both fields visibly filled, cannot submit, and retypes
username and password by hand -- on both steps of the flow.

Measured against the live login UI with a headless browser:

plain el.value = x + events (what an extension does) -> stays disabled
native prototype setter + input event -> enables
plain assignment fired before hydration -> stays disabled

useAutofillSync attaches a native input/change listener, which sits
below React's synthetic layer and fires regardless, and pushes the DOM
value into the form itself. It also reads the field once on mount, which
covers fill-on-page-load: those writes land before hydration, so no
event ever reaches a React listener.

This does not collapse the two-step flow -- a password manager still
acts once per page -- but fill-on-page-load now works unattended on both.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

Extensions fill a field by assigning `element.value` and then dispatching `input`/`change`. React installs its own `value` descriptor on the element, so that assignment also updates React's internal value tracker; when the event arrives React compares old and new, sees no difference, and suppresses the synthetic `onChange`. react-hook-form never learns the field was filled, `formState.isValid` stays false, the submit button stays disabled, and `handleSubmit` would send an empty value. The user sees both fields visibly filled, cannot submit, and retypes username and password by hand -- on both steps of the flow. Measured against the live login UI with a headless browser: plain `el.value = x` + events (what an extension does) -> stays disabled native prototype setter + input event -> enables plain assignment fired before hydration -> stays disabled useAutofillSync attaches a native `input`/`change` listener, which sits below React's synthetic layer and fires regardless, and pushes the DOM value into the form itself. It also reads the field once on mount, which covers fill-on-page-load: those writes land before hydration, so no event ever reaches a React listener. This does not collapse the two-step flow -- a password manager still acts once per page -- but fill-on-page-load now works unattended on both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fix: accept values written into the login fields by a password manager
All checks were successful
ci/woodpecker/push/docker Pipeline was successful
ci/woodpecker/pr/docker Pipeline was successful
ci/woodpecker/pull_request_closed/docker Pipeline was successful
b2c4be000c
Extensions fill a field by assigning `element.value` and then dispatching
`input`/`change`. React installs its own `value` descriptor on the
element, so that assignment also updates React's internal value tracker;
when the event arrives React compares old and new, sees no difference,
and suppresses the synthetic `onChange`. react-hook-form never learns
the field was filled, `formState.isValid` stays false, the submit button
stays disabled, and `handleSubmit` would send an empty value.

The user sees both fields visibly filled, cannot submit, and retypes
username and password by hand -- on both steps of the flow.

Measured against the live login UI with a headless browser:

  plain `el.value = x` + events (what an extension does) -> stays disabled
  native prototype setter + input event                  -> enables
  plain assignment fired before hydration                -> stays disabled

useAutofillSync attaches a native `input`/`change` listener, which sits
below React's synthetic layer and fires regardless, and pushes the DOM
value into the form itself. It also reads the field once on mount, which
covers fill-on-page-load: those writes land before hydration, so no
event ever reaches a React listener.

This does not collapse the two-step flow -- a password manager still
acts once per page -- but fill-on-page-load now works unattended on both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mael merged commit 0ca1540785 into main 2026-09-17 18:05:04 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
enpls/gnousconnect-frontend!2
No description provided.