Skip to content

feat(auth): enable password reset after cold boot - #16

Merged
barsnick merged 8 commits into
mainfrom
feature/password-reset-after-cold-boot
Oct 19, 2026
Merged

barsnick merged 8 commits into
mainfrom
feature/password-reset-after-cold-boot

Conversation

@FaHaGit

@FaHaGit FaHaGit commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

This PR adds a one-time WebUI credential reset option during the default 60-second window after a cold boot. It adds boot-marker handling, secure reset and setup request validation, and a login-screen countdown with recovery guidance.
Validated with the webserver build, 30 password-reset tests, and 8 UI tests.

@FaHaGit
FaHaGit requested a review from barsnick October 6, 2026 14:00
@FaHaGit
FaHaGit force-pushed the feature/password-reset-after-cold-boot branch 10 times, most recently from 43a0fd9 to 9ec57f0 Compare October 9, 2026 08:20
@FaHaGit

FaHaGit commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

feature/password-reveal should be also integrated in the SSH password configuration

Reject duplicate Host, Origin, Content-Type, and Content-Length headers, along
with folded header lines, invalid field names, and Transfer-Encoding. Return
HTTP 400 for these malformed requests so the WebUI parser does not accept
ambiguous or differently framed input.

Signed-off-by: Fabian Hartung <fabian.hartung@chargebyte.com>
Require a JSON Content-Type for the unauthenticated setup endpoint and
reject foreign or null Origin headers. This blocks simple cross-origin
form-style requests from creating the initial credentials.

Also validate the Host header against IP literals, localhost, the device
hostname and its .local name, the allow_origin host, and the new
allowed_hosts setting. A matching Origin alone does not protect against
DNS rebinding, because an attacker-controlled hostname can appear in both
headers. The allowlist is scoped to setup so customer aliases can still
serve the rest of the WebUI; unlisted hosts get 421 with
host_not_allowed.

Signed-off-by: Fabian Hartung <fabian.hartung@chargebyte.com>
Add the boot-scoped state that decides whether credentials may be reset.
The webserver reads the BBNSM boot marker as a little-endian 32-bit value
at offset 28: zero means a hard/cold boot, WARM means a regular software
reboot, and anything else leaves reset disabled. After persisting the
original classification, it clears exactly those four bytes without
truncating the NVMEM device and retries an interrupted clear on the next
start while keeping reset disabled for that boot.

Persist the deadline and a one-shot reset allowance in the systemd runtime
directory so restarting the WebUI neither restarts nor extends the window.
A non-blocking flock serializes initialization and reset across processes.
Without RUNTIME_DIRECTORY, reset stays disabled and normal login is
unaffected. Add the password_reset_window_seconds and
password_reset_boot_status_path settings and AuthManager::resetUser().

The HTTP endpoint and the login page integration follow separately.

Signed-off-by: Fabian Hartung <fabian.hartung@chargebyte.com>
Expose the reset allowance through POST /auth/reset and report its state
in GET /auth/status as passwordReset with available, remainingSeconds,
windowSeconds, and reason. The endpoint requires {"confirm":true} and is
covered by the same Content-Type, Origin, and Host checks as setup from
the start.

A successful reset removes the stored user, invalidates all sessions,
clears the requesting browser's session cookie, and closes open WebSocket
proxy sessions so that UI occupancy is released. WebSocket upgrades also
revalidate the session after the handshake, so a reset that races an
upgrade cannot leave an authenticated connection behind.

Signed-off-by: Fabian Hartung <fabian.hartung@chargebyte.com>
Show "Forgotten password?" on the login page with an info tooltip. While
reset is available, add a "Reset password" button with a local countdown
based on remainingSeconds and windowSeconds from /auth/status; the
browser does not poll. After confirmation the page calls /auth/reset and
opens the existing first-time setup screen without a time limit. Warm
boots, expired or used windows point to a power cycle instead.

Move formatAuthError into ui/authErrors.js so login and reset share the
messages, return to the login state when the server closes the WebSocket
because credentials were reset, and fall back to a reload if logout or
the following status request fails.

Signed-off-by: Fabian Hartung <fabian.hartung@chargebyte.com>
@barsnick
barsnick force-pushed the feature/password-reset-after-cold-boot branch from 9ec57f0 to 7b90a90 Compare October 19, 2026 11:20
Signed-off-by: Moritz Barsnick <moritz.barsnick@chargebyte.com>
Signed-off-by: Moritz Barsnick <moritz.barsnick@chargebyte.com>
Signed-off-by: Moritz Barsnick <moritz.barsnick@chargebyte.com>
@barsnick
barsnick force-pushed the feature/password-reset-after-cold-boot branch from 4d5bc4e to 7b42602 Compare October 19, 2026 12:14
@barsnick
barsnick merged commit 1cfa64e into main Oct 19, 2026
8 checks passed
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.

2 participants