Resetting the password — the canonical account-recovery action — leaves any already-issued refresh token valid, so an attackers session survives the reset.
Root cause
server/modules/auth/service.js:141-157 (resetPassword) verifies the OTP, updates the password, and deletes the OTP — but never clears security.refreshTokenHash. Compare the logout / theft paths that $unset it (auth/controller.js:120-124, middlewares/authMiddleware.js:71-73). The silent-refresh in authMiddleware (:47-100) keeps minting access tokens from the still-valid refresh token.
Failure scenario
An attacker has an active session on a victims account. The victim resets their password to recover. Because the stored refreshTokenHash is untouched, the attackers session keeps working via silent refresh. (Bounded: the single-slot refreshTokenHash is overwritten on the victims next successful login, controller.js:270-275 — but the reset itself performs no revocation and forces no re-login.)
Fix
On successful reset, $unset security.refreshTokenHash (and any session records), forcing re-login everywhere — the standard "reset ends other sessions" behavior.
Dedup
Distinct from #208/#211 (silent-refresh vs revoked-token, closed) and #301 (enumeration). No issue/PR covers reset-time revocation. Unreported.
Contributing as part of Elite Coders Summer of Code (ECSoC 2026).
Resetting the password — the canonical account-recovery action — leaves any already-issued refresh token valid, so an attackers session survives the reset.
Root cause
server/modules/auth/service.js:141-157(resetPassword) verifies the OTP, updates the password, and deletes the OTP — but never clearssecurity.refreshTokenHash. Compare the logout / theft paths that$unsetit (auth/controller.js:120-124,middlewares/authMiddleware.js:71-73). The silent-refresh inauthMiddleware(:47-100) keeps minting access tokens from the still-valid refresh token.Failure scenario
An attacker has an active session on a victims account. The victim resets their password to recover. Because the stored
refreshTokenHashis untouched, the attackers session keeps working via silent refresh. (Bounded: the single-slotrefreshTokenHashis overwritten on the victims next successful login,controller.js:270-275— but the reset itself performs no revocation and forces no re-login.)Fix
On successful reset,
$unset security.refreshTokenHash(and any session records), forcing re-login everywhere — the standard "reset ends other sessions" behavior.Dedup
Distinct from #208/#211 (silent-refresh vs revoked-token, closed) and #301 (enumeration). No issue/PR covers reset-time revocation. Unreported.
Contributing as part of Elite Coders Summer of Code (ECSoC 2026).