/settings has a password-change form in SettingsView.stx:149-220. The save handler is a stub — setTimeout(() => pwStatus.set('saved'), 350) per the comment at line 76. No API endpoint exists for it.
Scope
- New
app/Actions/Me/ChangePasswordAction.ts:- Auth-gated.
- Body:
current_password,new_password,new_password_confirmation. - Server-side verify
current_passwordagainst the user's stored hash via@stacksjs/auth. - On mismatch: 422 with field error.
- On success: hash new password (bcrypt, matching the existing scheme), update
users.password, return 200. - Optionally: revoke all other personal access tokens to force re-login on other devices.
- Route:
PATCH /api/me/passwordwith.middleware('auth').skipCsrf()(same pattern as the other/me/*endpoints). - Frontend: replace the
setTimeoutstub inSettingsView.stxwith a realauthFetch('/api/me/password', { method: 'PATCH', ... })call. Mirror the error / success states already wired in the form.
Acceptance
- User can change their password from
/settings. - Wrong current-password returns a field-level error, doesn't reveal whether the email is registered (constant-time compare).
- New password validated for length / complexity matching the registration rules.
- Optional flag in the UI: "Sign me out of all other devices" → triggers token revocation.
Files to touch
app/Actions/Me/ChangePasswordAction.ts— newroutes/api.ts— add the routeresources/components/Bench/Settings/SettingsView.stx:76— replace the setTimeout stub