Skip to content

v0.1.7: fix browser login form (multipart vs urlencoded)#5

Merged
hculap merged 2 commits into
mainfrom
fix/v0.1.7-browser-login-form-encoding
May 19, 2026
Merged

v0.1.7: fix browser login form (multipart vs urlencoded)#5
hculap merged 2 commits into
mainfrom
fix/v0.1.7-browser-login-form-encoding

Conversation

@hculap
Copy link
Copy Markdown
Owner

@hculap hculap commented May 19, 2026

The bug

The submit handler in `web_auth._HTML_FORM` was sending `multipart/form-data` (default when fetch sees a FormData body), but the server in `_make_handler` parses with `parse_qs` which only understands `application/x-www-form-urlencoded`. Result: every form submission came in with empty email + password, and the server returned 400 "Email and password are required."

Broken in 0.1.2 through 0.1.6. Nobody hit it earlier because:

  • Demo recordings used the CLI terminal path, not the browser form
  • The MCP `login_browser` tool always timed out in chat clients (~60s) before the user could click Submit
  • Once v0.1.6 made `login_browser` non-blocking and users could actually finish the form, the bug surfaced immediately

The fix

Wrap FormData in URLSearchParams in the JS submit handler. fetch then sends `application/x-www-form-urlencoded` — which is exactly what `parse_qs` expects:

```js
const params = new URLSearchParams(new FormData(form));
const resp = await fetch('/submit?state=' + ..., { method: 'POST', body: params });
```

Verified

End-to-end real-server test: simulate a JS form submit with urlencoded body, server now parses fields correctly and forwards to the eModul API (returns 401 for fake credentials, the expected downstream behavior).

Release

After merge: tag v0.1.7 → OIDC publish. Users on 0.1.6 must `pipx upgrade emodul` to use the browser login flow.

hculap added 2 commits May 19, 2026 16:04
…d urlencoded

The submit handler in web_auth._HTML_FORM was using:

    const formData = new FormData(form);
    fetch('/submit?...', { method: 'POST', body: formData });

When fetch sees a FormData body it sets Content-Type to
`multipart/form-data; boundary=...`. The server in _make_handler reads:

    body = self.rfile.read(length).decode('utf-8')
    form = parse_qs(body)
    email = (form.get('email') or [''])[0].strip()

parse_qs only understands application/x-www-form-urlencoded — given a
multipart body it returns an empty dict, so email/password come back
empty and the handler returns 400 "Email and password are required".

This was broken in every release that shipped the browser login form
(0.1.2 through 0.1.6). Nobody hit it earlier because the demos used
the CLI terminal path, and the MCP login_browser tool was always
timed out in chat clients before the user could even click Submit.
Once v0.1.6 made login_browser non-blocking so users could actually
finish the flow, the bug surfaced immediately.

Fix: wrap FormData in URLSearchParams in the JS submit handler, so
fetch sends `application/x-www-form-urlencoded` — which is exactly what
parse_qs expects.

Verified end-to-end: server now parses the credentials and forwards
to the eModul API (returns 401 for fake credentials, which is the
correct downstream behavior).
@hculap hculap merged commit 3ff0531 into main May 19, 2026
9 checks passed
@hculap hculap deleted the fix/v0.1.7-browser-login-form-encoding branch May 19, 2026 14:08
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