Add an admin password change flow in the web UI #65

Closed
opened 2026-08-07 13:11:13 +02:00 by clawbot · 1 comment
Collaborator

Part of the road to 1.0 (see #33).

There is no way to change the bootstrap admin password after first login. The profile page (internal/handlers/profile.go) is view-only. For an internet-facing single-admin service, being unable to rotate the initial password is a real gap.

Definition of done:

  • an authenticated, CSRF-protected form (on the profile page or a dedicated settings page) to change the current user's password
  • it verifies the current password with database.VerifyPassword before accepting a change
  • it stores a new Argon2id hash via the existing password helpers (internal/database/password.go)
  • success and error states are shown to the user
  • a handler test covers the happy path and a wrong-current-password rejection
Part of the road to 1.0 (see #33). There is no way to change the bootstrap admin password after first login. The profile page (`internal/handlers/profile.go`) is view-only. For an internet-facing single-admin service, being unable to rotate the initial password is a real gap. Definition of done: - an authenticated, CSRF-protected form (on the profile page or a dedicated settings page) to change the current user's password - it verifies the current password with `database.VerifyPassword` before accepting a change - it stores a new Argon2id hash via the existing password helpers (`internal/database/password.go`) - success and error states are shown to the user - a handler test covers the happy path and a wrong-current-password rejection
clawbot added this to the 1.0.0 milestone 2026-08-07 13:11:13 +02:00
Author
Collaborator

Implementation instructions

routes.go is free again (the interface refactor merged), so implement the admin password-change flow.

Scope: internal/handlers/profile.go (or a new handler file in handlers), internal/server/routes.go, templates/profile.html, and a handlers test. Reuse the existing password helpers — do not roll new crypto.

Behaviour:

  • Add a CSRF-protected, authenticated POST route to change the current user's password. Put it in setupUserRoutes under /user/{username} — that group already has CSRF, RequireAuth, and NoCache — e.g. POST /password.
  • Handler: enforce own-user (the path username must equal the session username, the same rule HandleProfile uses for its 403); parse current_password, new_password, confirm_password; verify the current password with database.VerifyPassword; require new_password to be non-empty and equal to confirm_password; hash the new password with the SAME mechanism used to create the admin user (see internal/database/password.go and how database.go bootstraps the admin) and persist it on the user row; re-render the profile page with a clear success or error message.
  • Add the change-password form to templates/profile.html (current / new / confirm fields plus the CSRF token, following how the other forms embed CSRF).

Definition of done:

  • an authenticated user can change their own password via a CSRF-protected form
  • a wrong current password is rejected with a clear message; a mismatched confirmation is rejected; neither changes the stored hash
  • the new password is persisted as a fresh hash via the existing helper (a subsequent login with the new password would succeed)
  • handler tests cover the happy path and the wrong-current-password rejection

Gates and process:

  • make fmt; validate with docker build . (must exit 0)
  • branch from main named issue-65-password-change; commit subject ends with (closes #65)
  • open a PR (base main) and comment on it with the diff summary and the docker build . result; no AI/tooling references
  • Note: main was just updated (delivery refactor + retention reaper + NoCache). If docker build fails on something unrelated to your change, check whether an unmodified origin/main builds, and if it does not, STOP and report that main is broken rather than trying to fix it.
## Implementation instructions `routes.go` is free again (the interface refactor merged), so implement the admin password-change flow. Scope: `internal/handlers/profile.go` (or a new handler file in `handlers`), `internal/server/routes.go`, `templates/profile.html`, and a handlers test. Reuse the existing password helpers — do not roll new crypto. Behaviour: - Add a CSRF-protected, authenticated POST route to change the current user's password. Put it in `setupUserRoutes` under `/user/{username}` — that group already has `CSRF`, `RequireAuth`, and `NoCache` — e.g. `POST /password`. - Handler: enforce own-user (the path username must equal the session username, the same rule `HandleProfile` uses for its 403); parse `current_password`, `new_password`, `confirm_password`; verify the current password with `database.VerifyPassword`; require `new_password` to be non-empty and equal to `confirm_password`; hash the new password with the SAME mechanism used to create the admin user (see `internal/database/password.go` and how `database.go` bootstraps the admin) and persist it on the user row; re-render the profile page with a clear success or error message. - Add the change-password form to `templates/profile.html` (current / new / confirm fields plus the CSRF token, following how the other forms embed CSRF). Definition of done: - an authenticated user can change their own password via a CSRF-protected form - a wrong current password is rejected with a clear message; a mismatched confirmation is rejected; neither changes the stored hash - the new password is persisted as a fresh hash via the existing helper (a subsequent login with the new password would succeed) - handler tests cover the happy path and the wrong-current-password rejection Gates and process: - `make fmt`; validate with `docker build .` (must exit 0) - branch from `main` named `issue-65-password-change`; commit subject ends with ` (closes #65)` - open a PR (base `main`) and comment on it with the diff summary and the `docker build .` result; no AI/tooling references - Note: `main` was just updated (delivery refactor + retention reaper + NoCache). If `docker build` fails on something unrelated to your change, check whether an unmodified `origin/main` builds, and if it does not, STOP and report that `main` is broken rather than trying to fix it.
sneak closed this issue 2026-08-07 23:23:05 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/webhooker#65