fix(security): invalidate user sessions on password change#1070
Conversation
…lities Add npm overrides for lodash@^4.18.1, lodash-es@^4.18.1, and brace-expansion@^2.0.3 to address high-severity CVEs in transitive dependencies (archiver → lodash, @llmist/cli → chevrotain → lodash-es). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CI Failures ResolvedFixes Applied
Root CauseThe Verification
🕵️ claude-code · claude-sonnet-4-6 · run details |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
nhopeatall
left a comment
There was a problem hiding this comment.
Summary
LGTM — Clean, well-scoped security fix that correctly invalidates user sessions on password change.
What I verified:
- Correctness:
deleteUserSessionsis called afterupdateUsersucceeds, gated byupdates.passwordHash !== undefined— only triggers on actual password changes, not name/email/role updates. - Repository function: Proper Drizzle ORM usage with
and(eq, ne)for theexcludeTokenbranch. Theneimport was already present in the file. - No missed call sites: Password changes only occur in
users.update—login.tsonly compares passwords. No other mutation path needs this fix. - Test coverage: Three new router tests (password change → invalidates, name change → no invalidation, email change → no invalidation) plus three repository tests covering both code paths.
- Mock reset:
clearMocks: truein vitest config ensuresmockDeleteUserSessionsis properly reset between tests; thebeforeEachre-sets the default resolved value. - Design decisions: Not wrapping in a transaction is reasonable — if session deletion fails after password update, security posture is still improved (password changed, sessions expire naturally). Invalidating all sessions when admin changes their own password is correct security behavior.
- Package overrides: lodash 4.18.1 and brace-expansion 2.0.3 are valid published versions (verified against npm registry). These are appropriate security dependency bumps.
- CI: All 7 checks passing.
🕵️ claude-code · claude-opus-4-6 · run details
Keep all three overrides (lodash, lodash-es, brace-expansion) from the PR branch when merging dev. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
nhopeatall
left a comment
There was a problem hiding this comment.
Summary
LGTM — Clean, well-scoped security fix that correctly invalidates all sessions when an admin changes a user's password.
Verified:
deleteUserSessionsis correctly placed in the repository layer alongside existing session operations (deleteSession,deleteExpiredSessions), following the same patterns (usesgetDb(), samesessionstable, consistentPromise<void>return).- The router checks
updates.passwordHash !== undefined(notinput.password), which correctly gates on the actual DB update field, ensuring session invalidation only triggers when a password hash was actually computed and written. - The
excludeTokenbranch uses properand(eq(...), ne(...))composition with operators already imported — no new dependencies introduced. - The FK schema (
sessions.userId→users.idwithonDelete: 'cascade') meansdeleteUseralready handles session cleanup on user deletion; this new function addresses the distinct case of password change without user deletion. - Decision not to wrap in a transaction is sound: if
deleteUserSessionsfails afterupdateUsersucceeds, the password is still changed (security improved) and sessions expire naturally. - Tests cover both layers (repository + router) with positive and negative cases.
- All 7 CI checks pass.
The package.json override additions (lodash, brace-expansion) are unrelated to the security fix but are harmless dependency resolution overrides.
🕵️ claude-code · claude-opus-4-6 · run details
Summary
deleteUserSessions(userId, excludeToken?)repository function tousersRepository.tsthat deletes all sessions for a given userdeleteUserSessionsfrom theusers.updatetRPC mutation after a successful password hash update, ensuring all existing sessions are invalidatedSecurity Fix
When an admin changed a user's password via
users.update, all existing sessions for that target user remained valid — allowing continued access with the old credentials. After this fix, all sessions for the target user are deleted immediately after the password is updated.Reference: https://trello.com/c/eL6bwZFo/573-2-sessions-not-invalidated-on-password-change-src-api-routers-usersts121-when-an-admin-changes-a-users-password-all-existing-ses
Key Decisions
excludeTokenused in the router: Since the mutation is admin-only and typically targets another user, all of the target user's sessions are invalidated. If an admin changes their own password, they will be logged out — this is correct security behavior.deleteUserSessionsfails afterupdateUsersucceeds, the password is still updated, improving security posture. Sessions will expire naturally.excludeTokenparameter kept for future use: The repository function supports an optionalexcludeTokenfor cases where the caller wants to preserve their own session.Test plan
deleteUserSessionsunit tests: deletes all sessions for user, handlesexcludeTokenconditionalwarn, noterror)🕵️ claude-code · claude-sonnet-4-6 · run details