fix(auth): revoke personal access tokens when a password is reset
Merged
JU
1d ago+469+469+012345678901234567890123456789-17-17-01234567890123456789
Description
Problem
- A personal access token (PAT) created before a password reset still returned 200 after the reset.
- Applies to the self-service password change and to the admin reset.
- The admin reset dialog did not tell the admin that the user is signed out.
- The admin reset result dialog showed both Cancel and Done.
Security rationale
- A password reset is the recovery step after a credential leak.
- A PAT is a long-lived bearer credential. If it survives the reset, an attacker keeps access.
- #16049 signs out sessions on reset. This change closes the same gap for PATs.
Backend
- New
revoke_all_pats_for_user__no_commitindb/pat.py: setsis_revokedandexpires_at = nowon every active PAT of the user, all types. Returns the count. UserManager._updateoverride: every password write revokes the user's PATs in the same commit as the new hash. Sessions are still revoked after the commit (revoke_sessions).- Paths that write a password (the same as #16049):
- Admin reset:
POST /password/reset_password→reset_password_as_admin - Self-service change:
POST /password/change-password→change_password_if_old_matches - Forgot-password token flow:
POST /auth/reset-password→ fastapi-usersreset_password
- Admin reset:
- A wrong current password revokes nothing.
- API keys (service accounts) are not affected.
Behavior change for users
- After any password reset or change, all of the user's PATs stop working (401/403).
- Scripts, CLIs and integrations that use a PAT break after a reset. The user must create a new PAT.
- Craft sandbox PATs are revoked too. A RUNNING sandbox gets a new PAT at its next reservation (
reserve_sandbox__no_commit); the proxy reads the stored PAT on each request.
UI
- Admin reset dialog: the description now says the user is signed out of all sessions and their PATs are revoked.
- Admin reset result: Cancel is hidden. Only Done shows.
- Change-password dialog: new description says other sessions are signed out and PATs are revoked.
- Settings page clears the cached token list and refetches it after a password change.
- New copy is translated in every locale.
Fixes ENG-4424
Screenshots
Admin reset confirm
| Before | After |
|---|---|
Admin reset success
| Before | After |
|---|---|
Self-service change password
| Before | After |
|---|---|
How Has This Been Tested?
- External dependency unit (
tests/external_dependency_unit/auth/test_pat_revocation.py, real Postgres + Redis):- Admin reset revokes USER and CRAFT PATs, another user's PAT stays valid.
- Self-service change revokes PATs.
- Forgot-password
reset_password(real token) revokes PATs. - Wrong current password keeps PATs.
- Already-revoked PATs are not counted again.
- A failed PAT revocation keeps the old password (one commit). Fails if the revocation commits separately.
- External dependency unit (
tests/external_dependency_unit/craft/test_sandbox_pat.py): password change with a RUNNING sandbox, thenensure_sandbox_readystores a new valid PAT. Fails without the reservation hook. test_session_revocation.py,test_auth_audit_events.py, and the Craft sandbox lifecycle, provisioning, and PAT resolver suites still pass (62 passed, 1 known xfail).- Integration (
tests/integration/tests/security/test_session_revocation_on_reset.py): new cases for admin reset and self-service change (PAT 200 before, 401/403 after; admin's own PAT stays valid). Not run locally; CI runs them. - Jest (
UserActionModals.test.tsx): confirm state shows the new copy and Cancel; success state shows Done and no Cancel. The test fails withouthideCancel. tscclean.pre-commitclean, including the i18n copy-change check.
Additional Options
- [Optional] Please cherry-pick this PR to the latest release version.
🤖 Generated with Claude Code
Summary by cubic
Revokes all of a user's personal access tokens whenever their password is reset or changed, at the same three hook points that already sign out sessions: admin reset, self-service change, and the forgot-password flow. Previously a PAT minted before a reset kept working, leaving a long-lived credential active after a suspected leak.
A wrong current password revokes nothing. API keys are unaffected. Craft sandbox PATs are revoked too; a new one is minted on the next sandbox run.
UI
- Admin and change-password dialogs now state that sessions end and PATs are revoked, in every locale.
- Admin reset result shows only Done; Cancel is hidden.
- The settings page refreshes the token list after a password change.
Migration
- After any reset, scripts, CLIs, and integrations using PATs return 401/403 and require minting a new token.
Summary by CodeRabbit
-
Security
- Password resets and password changes now revoke active personal access tokens and sign users out of other sessions.
- Service-account API keys and the current session remain unaffected.
-
Craft Sandboxes
- Running sandboxes receive a replacement personal access token at their next reservation if their previous token was revoked.
-
User Experience
- Password-management warnings explain session sign-out and token revocation across supported languages.
- Token lists refresh after password changes, and reset dialogs provide clearer completion behavior.