Skip to content

feat: let staff force a password reset without emailing a link - #23

Merged
lmajano merged 1 commit into
developmentfrom
claude/force-password-reset-toggle
Sep 1, 2026
Merged

feat: let staff force a password reset without emailing a link#23
lmajano merged 1 commit into
developmentfrom
claude/force-password-reset-toggle

Conversation

@lmajano

@lmajano lmajano commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Description

Companion feature to #22 (merged): a staff-triggered "force password reset" toggle for managed users.

Previously isPasswordReset could only be set at seed time (a freshly-seeded admin) or via the existing "send reset email" flow (Users.bx#resetPassword()), which emails a link the user must click. There was no way for an admin to force an existing user to change their password on their very next login without waiting on that email.

What this adds:

  • Users.bx#forcePasswordReset() — a new @secured("users:admin") action that sets isPasswordReset=true directly (no email sent), guarded by CSRF like the handler's other mutating actions, and blocked against self-targeting via the existing ensureNotSelf() helper. Audit-logged via the existing auditLogService, matching the pattern of setStatus()/verify().
  • Router.bx: POST users/:userId/force-password-reset
  • users/detail.bxm: a new "Force password reset" row next to the existing "Send reset email" row in the Security tab, disabled once already pending (Already required on next login.).
  • UserDetailForm.js: a forcePasswordReset() Alpine action mirroring the existing resetPassword()/verifyUser() actions.

Since the login flow (#22) already checks isPasswordReset and redirects a correct-password login into the reset-password flow instead of the dashboard, this toggle takes effect on the targeted user's very next login attempt — no separate wiring needed on that side.

Verification

  • npm run build — clean.
  • npm run lint — clean on the changed file (UserDetailForm.js); pre-existing lint errors in an unrelated file, AuditLogForm.js, are untouched by this PR.
  • No dedicated handler test added: this repo has no handler-level test coverage for any of Users.bx's existing sibling actions (setStatus, resetPassword, verify, etc.), so this follows that precedent rather than introducing a one-off pattern.
  • I could not run the CommandBox/BoxLang test suite locally in this sandbox (same ForgeBox TLS limitation noted in fix: isPasswordReset locks users out of login instead of forcing a reset #22box install can't fetch ColdBox/TestBox here). The underlying "a correct password on an isPasswordReset account redirects to the reset-password flow instead of the dashboard" behavior is already covered end-to-end by AuthTest.bx from fix: isPasswordReset locks users out of login instead of forcing a reset #22, which passed 145/145 in CI on that PR. This toggle only flips the same flag through a new admin-facing entry point, so no new server-side behavior needs separate coverage beyond that existing test — CI will confirm nothing regressed.

Issues

No existing tracked issue — ported from an equivalent feature already built and running in a downstream app (bx-sites-cloud) that was scaffolded from this template, adapted to this repo's actual Users.bx/detail.bxm/UserDetailForm.js structure (confirmed to already have the getManagedUser()/ensureNotSelf()/buildDetailPayload()/auditLogService helpers this feature relies on).

Type of change

  • New Feature

Checklist

  • I have commented my code, particularly in hard-to-understand areas (the doc comment on forcePasswordReset() explains how it differs from resetPassword() and how it interacts with the login flow from fix: isPasswordReset locks users out of login instead of forcing a reset #22)
  • I have made corresponding changes to the documentation — N/A, no user-facing docs reference the admin user-management UI's individual actions
  • I have added tests that prove my fix is effective — see Verification above for why this follows existing precedent instead
  • New and existing unit tests pass locally with my changes — not run locally (see Verification); CI runs the full suite with real MySQL and I'll watch it through to green

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ft3NTvnLUZ99axxmLn6Cxf


Generated by Claude Code

Adds a staff-triggered "force password reset" toggle for managed users,
companion to the isPasswordReset login-lockout fix merged in PR #22.

Previously isPasswordReset could only be set at seed time or via the
existing "send reset email" flow (Users.bx#resetPassword(), which emails
a link the user must click). There was no way for an admin to force an
existing user to change their password on their very next login without
waiting on that email.

- Users.bx#forcePasswordReset(): sets isPasswordReset=true directly (no
  email), guarded by @secured("users:admin"), CSRF-checked like the
  handler's other mutating actions, and blocked against self-targeting
  via the existing ensureNotSelf() helper. Now that the login flow (PR
  #22) checks isPasswordReset and redirects a correct-password login
  into the reset-password flow, this takes effect on the user's very
  next login.
- Router.bx: POST users/:userId/force-password-reset
- users/detail.bxm: a "Force password reset" row next to the existing
  "Send reset email" row, disabled once already pending.
- UserDetailForm.js: forcePasswordReset() action, mirroring the existing
  resetPassword()/verifyUser() actions.

No dedicated test added: this repo has no handler-level test coverage
for any of Users.bx's existing sibling actions (setStatus, resetPassword,
verify, etc.), so this follows that precedent. Verified via `npm run
build` and `npm run lint` (clean on the changed file); the underlying
isPasswordReset-triggers-a-redirect behavior is already covered by
AuthTest.bx from PR #22.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ft3NTvnLUZ99axxmLn6Cxf
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

Test Results

145 tests   145 ✅  10s ⏱️
 16 suites    0 💤
  1 files      0 ❌

Results for commit 0fe6958.

♻️ This comment has been updated with latest results.

@lmajano
lmajano merged commit 1e48dad into development Sep 1, 2026
4 checks passed
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.

2 participants