Summary
OAuth access and refresh tokens were not revoked when the user changed, reset, or
recovered their password, leaving an attacker-issued OAuth grant valid after the user
believed they had locked the attacker out.
Details
revokeAllOAuthTokensByUser in the users service was an empty stub being called from
passwordChange, passwordForgot, and passwordReset. It now delegates to
OAuthToken.revokeAllByUser(userId), which deletes the rows and invalidates the
related auth caches. All three reset/recovery flows now consistently revoke refresh
tokens (GHSA-r989-7g3j-wjhw), OAuth tokens (this advisory), and rotate
token_version.
Impact
Persistent unauthorized access through previously issued OAuth tokens after a
documented security event (password change, forgot, or reset).
Credit
This issue was reported by @bugbunny-research.
References
Summary
OAuth access and refresh tokens were not revoked when the user changed, reset, or
recovered their password, leaving an attacker-issued OAuth grant valid after the user
believed they had locked the attacker out.
Details
revokeAllOAuthTokensByUserin the users service was an empty stub being called frompasswordChange,passwordForgot, andpasswordReset. It now delegates toOAuthToken.revokeAllByUser(userId), which deletes the rows and invalidates therelated auth caches. All three reset/recovery flows now consistently revoke refresh
tokens (GHSA-r989-7g3j-wjhw), OAuth tokens (this advisory), and rotate
token_version.Impact
Persistent unauthorized access through previously issued OAuth tokens after a
documented security event (password change, forgot, or reset).
Credit
This issue was reported by @bugbunny-research.
References