Step #5429 "Reset by email: request, link and new-password page" of
milestone 497 "Passwords: change your own, and reset it by email".
- Sign-in page: "Forgot your password?" while a mail server is set.
- /forgot takes a username or email and answers the same either way; only
an active local account with a real address gets mail. Requests count
per address on their own throttle, so they never lock out sign-in.
- password_resets (migration 0039): a one-time key, stored as its sha256,
working for an hour; asking again replaces it.
- /reset/<key>: the new password, then signed straight in; every other
browser is signed out (sessions_since). Bad keys count as invite keys do.
- Email "[Steward] Reset your password", in the account's own zone.
- Audited: user.password_reset_requested, user.password_reset_by_email.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#5035 "Sessions: lifetime applied, role and is_active re-read per request,
logout is a POST, failed logins throttled".
- steward/core/proxy.py: trusted_value / is_https / client_ip read
X-Forwarded-Proto and -For only as far as the trusted proxy hops
(Settings → General, default 1, 0 = trust none).
- The session cookie is Secure, and HSTS is sent, only on requests that
arrived over HTTPS by that rule; plain-HTTP and LAN use are unchanged
and nothing redirects.
- A login lasts Settings → General's session lifetime from when it began
(session.permanent + a login_at stamp checked every request — the
cookie's own age slides on every response). A deleted or deactivated
user is logged out on their next request; a role change applies at once.
- Logout is a POST (a form in the sidebar); GET /logout is gone.
- Failed logins pause an address after login.max_failures within
login.lockout_minutes (Settings → General; defaults 5 in 15 min),
keyed by address so nobody can lock a user out by name. Failures are
audited as auth.login_failed with the address.
- to_access_cfg projects the new settings into app.config at startup and
on save, beside the existing projections.
- Integration tests seed real users and a login_at, since every request
now re-reads the session's user.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>