Skip to content

Password change and admin reset for local accounts #117

Closed

Description

@amar-python

Raised by the product owner on 27 Oct, while working on #116. Relates to BR-36 and the ART Milestone 2 local accounts (#92).

Problem

Local accounts (#92, PR #98) have no way to:

  • change a password. A signed-in user can't set a new one.
  • reset a forgotten password or unlock an account. An admin can't do this for a user.
  • recover from a lost first-admin password. Rerunning scripts/create_tenant_admin.py fails once the tenant exists, so there's no way back short of editing the database.

On 27 Oct the dev admin's password ended up in a chat transcript, and today there's no supported way to rotate it.

Proposed scope (to be confirmed)

  1. Change your own password. POST /api/auth/password takes the current and new password, checks the new one against the same 12-character minimum, and revokes the user's other sessions.
  2. Admin reset. A tenant_admin sets a new password for a member of their own tenant, and the reset is audited. Add the action to docs/ROLES.md and tests/test_roles.py.
  3. Break-glass CLI. scripts/reset_password.py --username <u> for an operator with database access. It's the only way back if every admin is locked out, and it also clears any lockout (M2a-2: lock accounts after repeated failed sign-ins (BR-36) #102).
  4. Frontend. A change-password form, reached from the header menu (M2e: frontend sign-in and tenant-scoped session (BR-36) #96).

Verified by

Integration tests:

  • the old password stops working and the new one works;
  • other sessions are revoked;
  • a non-admin can't reset someone else's password;
  • an admin can't reset a user in another tenant;
  • the CLI resets the password and clears a lockout.

Plus vitest for the form.

Done when

Merged with the tests above passing, and the evidence attached here (closure rule).


Generated by Claude Code

Activity

  1. added this to the ART extension milestone on Oct 26, 2026
  2. added a commit that references this issue on Oct 27, 2026
  3. amar-python commented on Oct 27, 2026

    @amar-python
    OwnerAuthor

    Evidence for closure. Merged by the product owner's instruction on 27 Oct 2026.

    • PR: feat(auth): password change, admin reset and break-glass reset (#117) #122, merge commit a5d8544 on main.
    • GitHub CI on head 0dbad37: 3 of 3 passed (free-tier, integration, python-validator-tests).
    • Local CI replica on the same code: 308 passed, 0 failed, 0 skipped. Evals 26/26.
    • The tests this issue asked for (tests/test_password_reset.py, tests/test_roles.py):
      • the old password stops working and the new one works;
      • other sessions are revoked, and the current one stays;
      • a non-admin can't reset someone else's password (403, and every role is tried against the route);
      • an admin can't reset a user in another tenant (404);
      • the break-glass script resets the password, clears a lockout and ends sessions.
    • Also tested:
      • a wrong current password is 400 and counts towards the lockout;
      • resets are written to the tenant's audit log;
      • a user who is also in another tenant is refused (409).
    • Mutation checks: 7 controls were each removed in turn, and at least one test failed every time.
    • Frontend: vitest 126 passed, including 4 for the change-password form.

    Product owner decisions:


    Generated by Claude Code

  4. amar-python commented on Oct 27, 2026

    @amar-python
    OwnerAuthor

    On 27 Oct the product owner changed the dev admin's password (tenant dev) through the new change-password dialog. That replaces the password exposed in a chat transcript that morning.

    • Evidence: the API log on the dev machine at about 18:20 shows POST /api/auth/logout 200 and then POST /api/auth/login 200. The product owner confirms that sign-in used the new password.
    • Limit: the change request itself wasn't logged. It went to a leftover API process (PID 11404, started 02:12) that had already reloaded the merged code. That process was then stopped. See the Frontend shows 'Failed to fetch' when the API is down; the local API stopped silently once #118 comment of 27 Oct.

    Generated by Claude Code

  5. amar-python commented on Oct 27, 2026

    @amar-python
    OwnerAuthor

    Product owner decision, 27 Oct 2026: organisations (tenants) must stay independent. A tenant admin may act only on users within their own organisation.

    This confirms the behaviour merged in #122, so no code change is needed:

    • A user who isn't in the admin's tenant: the reset returns 404, which doesn't reveal that the user exists.
    • A user who is also in another tenant: the reset is refused with 409. Only an operator with server access can reset them, using scripts/reset_password.py.

    Covered by tests/test_password_reset.py: test_admin_cannot_reset_a_user_in_another_tenant and test_user_who_is_also_in_another_tenant_is_refused. The mutation check that removed the refusal made the second test fail.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions