AZ-007 — Account settings and password change
Status
Done
Context
Read README. Settings already has device language/theme controls and is protected by AZ-006. Users/session API and common password policy exist. There is no profile-update or password change operation. This increment gives settings an account purpose without inventing admin/RBAC.
Goal
Deliver a small mobile account page with display-name editing, persisted user preferences and secure password change, preserving explicit device/account preference precedence.
Requirements
- Show current email read-only and editable display name, language and appearance. No arbitrary
unused settings. Split Account/Preferences/Security into simple flow sections/cards rather than
dense tabs/table/dialogs on small screens. Menu registration remains the same
/settingsdeclaration; localize all new copy and field/helper/notification text. - Add authenticated
PATCH /api/users/meaccepting only bounded display name and supported language/appearance fields, returning the minimal user DTO. Reject identity/role/email/hash fields, unknown preferences and another-user ID. Backend authenticates and derives identity from session; no/users/:idpublic editing or admin capability. - Store preferences with constraints through Drizzle migrations/repositories if not already created in AZ-005. DTO update/current-user retrieval expose them. New user null/unset preferences mean account has not chosen yet, not an implicit browser-language mutation.
- Preference precedence: anonymous startup uses AZ-001 device/browser/default. At login a non-null saved account value wins; unset account value keeps device choice. At logout reset application UI to the anonymous device preference, not the last user's settings. Retain only device language/theme in localStorage; do not write account identity or silently persist one user's account preference as another user's device choice.
- Anonymous changes save device preference immediately. Authenticated Settings changes update backend first, then apply Redux/i18n/theme and query DTO on success. A failed save preserves prior effective account values and allows retry; form draft is local. If account value is unset, first successful change establishes it. No bidirectional effect loop or duplicate Redux user DTO. Document background language reapplication if me refetch returns updated preference.
- Add authenticated
POST /api/users/me/passwordwith current and new password, 204 on success. Reuse common policy/hash verification, CSRF/origin, no-store and throttling. Wrong current password gives safe generic field/use-case failure without disclosing anything additional; invalid policy gives safe validation issues. Client confirmation is a usability check; backend still enforces the real policy. - Verify current credential and update hash/revoke all sessions atomically. After success return the user to login, clear private caches and session cookie, and state that re-login is needed. Do not retain current session through an undocumented exception. Concurrent password updates must not allow an old-current-password request to overwrite a newer change; test transaction or conditional update behavior. Recovery tokens are invalidated too once AZ-010 exists.
- Ensure in-flight login/session-store saves cannot resurrect a revoked session after password change. Use a concrete credential/session version or equivalent transactional revalidation; verify old credential verification followed by a concurrent change cannot mint a valid session.
- Add API tests for unauthenticated/CSRF/validation/own-user enforcement and deterministic failure responses. Real PostgreSQL tests prove persisted preferences, change/revocation and concurrency. E2E uses distinct users and changes one password without breaking other projects' fixtures. Existing message seed remains unaffected; migrations prove fresh/upgrade/rerun if schema changes.
Non-goals
No email change/verification, avatar upload, billing/business settings, organization membership, roles/admin screen, other-user editing, invitation/registration, or runtime business branding.
Implementation guidance
Extend Settings and auth/user endpoint modules, application services and concrete repositories; keep networking in API boundary and DTO in Query. Let UI drafts differ from saved settings without globally changing language mid-failed-save. Profile controls use labels/autocomplete, touch targets and field focus from AZ-003. Backend password-operation logic is separate from HTTP/session adapter but coordinates durable hash change and revocation within a real repository transaction capability. Avoid a generic transaction/unit-of-work framework for this one concrete atomic operation.
Acceptance criteria
- Profile edits/preferences persist across session/reload; only the session owner is editable.
- Device/account/unset/logout precedence is deterministic and tested across two users.
- Failed preference save does not leave store/theme/i18n/DTO inconsistent.
- Password change enforces current credential/policy, invalidates every session and requires login with the new password; concurrency cannot restore old credentials.
- Settings works at mobile widths, with usable keyboard focus, validation and long labels.
Verification
pnpm db:generate
pnpm quality
pnpm test
pnpm build
pnpm test:coverage
pnpm validate:docker
Run migration generation only for schema changes. Add frontend preference-merge/unit/RTL tests, backend injection and real transaction tests, and Playwright profile/password-change journeys in both viewport projects. Check 320px, 360×800, tablet/desktop, virtual keyboard and zoom manually. Never change a developer's real credentials as a test; use the dedicated fixture lifecycle.
Documentation
Update OpenAPI, architecture (account/device preference source and password transaction), development (Settings behavior/provisioning recovery), testing (two-user/concurrency cases), current-state/handover. Document unsupported email/admin/business operations explicitly.
Dependencies
AZ-006 and its prerequisites. Password recovery is AZ-010, not required for this increment.
Future considerations
A future business settings area needs ownership and authorization distinct from personal account settings. Static theme brand and personal appearance are not tenant branding.