Skip to content

Server, Admin: Add password reset - #346

Merged
lehni merged 1 commit into
feature/totpfrom
feature/password-reset
Oct 17, 2026
Merged

lehni merged 1 commit into
feature/totpfrom
feature/password-reset

Conversation

@lehni

@lehni lehni commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Builds on #342. Users of any UserMixin model can reset a forgotten password through a link sent by email, in Dito.js' admin and in other frontends through the same actions.

Server:

  • app.auth.passwordReset: { confirmUrl, expires = '1h' }. confirmUrl is the page the email links to, with a {token} placeholder.
  • Tokens are random, stored as HMAC hashes in the shared credential table, single-use and scoped to the user model, and expire after expires.
  • post password-reset with { username } always gives the same answer, whether the account exists or not, and sends at most one email a minute per user through sendPasswordReset(ctx, user, confirmUrl), which apps override to send the email themselves.
  • post password-reset-confirm with { token, password } sets the password with the model's validation and deletes the tokens. It doesn't log in.
  • Sessions end when their user's password changes: they store a fingerprint of the password hash and drop the user when it doesn't match any more. Changing your own password keeps your session. Sessions from before this change stay valid.
  • One confirmUrl for the whole app; only controllers that route the reset actions need to override sendPasswordReset(). Sending can't change the answer, also when the method isn't async, and transacted actions send after the commit.
  • Changing the password also invalidates open reset links.
  • General rate limiting stays with the app or proxy, as the answer's timing may differ.

Admin: login: { passwordReset: true } adds "Forgot password?" to the login dialog. The emailed link opens the admin with #password-reset=<token> (a fragment, so the token stays out of server and proxy logs) and asks for the new password, again with the token kept if the server rejects it.

Behaviour change: changing a user's password ends that user's other sessions.

@lehni
lehni added this pull request to stack #341 October 17, 2026 02:04
@lehni
lehni force-pushed the feature/password-reset branch from 474a587 to 8a7b494 Compare October 17, 2026 03:07
- Add app.auth.passwordReset: emailed single-use links through the app's UsersController.sendPasswordReset(), with tokens stored hashed as credentials, the same answer for unknown usernames and one email a minute
- End the other sessions of users whose password changes, through a hash of their hash in the session, keeping the session that changes it
- Add login.passwordReset to the admin: a Forgot password? button in the login dialog, and a dialog for the new password when opened with #password-reset=, asking again after validation errors
- Keep dialog buttons wider than their components off the edges, and notify the message of failed login requests
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