Summary
A stolen refresh token survived a password-forgot flow and could be used to mint fresh
JWTs even after the user reset their password.
Details
passwordChange and passwordReset deleted the user's refresh tokens, but
passwordForgot only rotated token_version and revoked OAuth tokens — it did not
call UserRefreshToken.deleteAllUserToken(user.id). An attacker holding a captured
refresh cookie could still exchange it for a new access token after the victim
triggered the recovery flow.
Impact
Persistent unauthorized access after password recovery. Once a refresh token leaks, the
documented "Forgot password" recovery flow did not in fact revoke the attacker's
session.
Credit
This issue was reported by @bugbunny-research.
References
Summary
A stolen refresh token survived a password-forgot flow and could be used to mint fresh
JWTs even after the user reset their password.
Details
passwordChangeandpasswordResetdeleted the user's refresh tokens, butpasswordForgotonly rotatedtoken_versionand revoked OAuth tokens — it did notcall
UserRefreshToken.deleteAllUserToken(user.id). An attacker holding a capturedrefresh cookie could still exchange it for a new access token after the victim
triggered the recovery flow.
Impact
Persistent unauthorized access after password recovery. Once a refresh token leaks, the
documented "Forgot password" recovery flow did not in fact revoke the attacker's
session.
Credit
This issue was reported by @bugbunny-research.
References