Mirrored from GitHub github.com/roostorg/coop

(auth) Invalidate sessions on password change (#778) master

* [Security] Invalidate sessions on password change (GHSA-g5xq-67g7-36r2) Resetting a password did not clear the user's PG-stored sessions (connect-pg-simple, 30-day maxAge), and passport.deserializeUser looks users up by id only. A phished/attacker session therefore survived a password reset for up to 30 days, even after the legitimate user took the expected recovery action. Add deleteSessionsForUser(), which removes the user's rows from public.session (matched on passport's `sess -> 'passport' ->> 'user'`), and call it from both password-change paths: - resetPasswordForToken: deletes all of the user's sessions. - UserApi.changePassword: deletes all *other* sessions, preserving the caller's own (via the request's sessionID) so they aren't logged out mid-action. No DB migration; deserializeUser is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Drop advisory ID from code comments Keep the explanatory comments; remove the GHSA reference so it isn't exposed in source while the advisory is unpublished. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Drop file-path reference from sessionPersistence comment Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Make password-change + session invalidation atomic Wrap the password update and session purge in a single transaction (via makeKyselyTransactionWithRetry) in both resetPasswordForToken and UserApi.changePassword. Previously these were separate commits, so a failure after the password update left the user's sessions alive — the exact persistence the fix is meant to prevent. Also narrow the test's eslint-disable for the unused mock constructor args from a block disable to per-line comments. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>