OSV 1.4.0 · github-reviewed · 修改于 2026-07-08 04:56
发布时间
2026-07-08 04:56
GitHub 审查时间
2026-07-08 04:56
NVD 发布时间
—
源文件
advisories/github-reviewed/2026/07/GHSA-2vg6-77g8-24mp/GHSA-2vg6-77g8-24mp.json
Users are affected if all of the following are true:
secondaryStorage on betterAuth(...) (Redis, KV, or any external session cache).session.storeSessionInDatabase is left unset or set to false (the default).admin plugin and calls auth.api.removeUser(...) or authClient.admin.removeUser(...).anonymous plugin and exposes /delete-anonymous-user or relies on the after-link hook to clean up the anonymous user.@better-auth/scim plugin and exposes DELETE /scim/v2/Users/:userId.If storeSessionInDatabase is true, sessions are also written to the database, and the database delete cascades; users are not affected.
Fix:
better-auth@<patched-version> or later (and @better-auth/scim@<patched-version> if they use SCIM).When secondaryStorage is configured and storeSessionInDatabase is false, three user-deletion endpoints in better-auth plus one in @better-auth/scim call internalAdapter.deleteUser(userId) without first calling internalAdapter.deleteSessions(userId). The deleted user's session payload (which carries a cached user object) remains in secondary storage, and internalAdapter.findSession(token) keeps returning it as a valid session until the session TTL elapses (default 7 days).
The vulnerable call sites are:
admin plugin's removeUser (packages/better-auth/src/plugins/admin/routes.ts:1463).anonymous plugin's self-delete endpoint ().packages/better-auth/src/plugins/anonymous/index.ts:222anonymous plugin's after-link hook (packages/better-auth/src/plugins/anonymous/index.ts:325).@better-auth/scim's DELETE /scim/v2/Users/:userId (packages/scim/src/routes.ts:1019).Working callers that already do the right thing: the core /delete-user self-delete and /delete-user/callback (packages/better-auth/src/api/routes/update-user.ts:551).
The fix shape extends each vulnerable caller to invoke deleteSessions(userId) before deleteUser(userId). The architectural follow-up centralizes the cleanup inside deleteUser itself or introduces a single deleteUserAndSessions orchestrator so future callers cannot regress this contract.
Fixed in better-auth@<patched-version> and @better-auth/scim@<patched-version>. All four user-deletion call sites now invoke deleteSessions(userId) before deleteUser(userId) so sessions are evicted from secondary storage at the same time the user row is removed.
If users cannot upgrade immediately:
session.storeSessionInDatabase: true. Subsequent user-delete writes reach the session table and the database cascade removes rows. Increases write volume for high-throughput sessions but eliminates the gap.auth.api.removeUser, also call auth.api.revokeUserSessions({ body: { userId } }), which uses deleteSessions internally.auth.api.revokeUserSessions(...) after the SCIM DELETE.onLinkAccount, explicitly call internalAdapter.deleteSessions(anonymousUser.user.id) before allowing the new session to be issued.getSessionFromCtx until the session TTL elapses (default 7 days). Within that window, the deleted user retains their pre-existing read and write surface.Reported by @iruizsalinas.