Skip to content

session-list-revoke: sys_session.revoked_at/revoke_reason are declared readonly "System-managed" but the interactive-revoke path deletes rather than tombstones — ADR-0069 D4 revoke-audit trail is effectively inert #7732

Description

@huangyiirene

Symptom

sys_session.revoked_at and revoke_reason are declared readonly:true and documented as "System-managed", intended to capture every revocation cause — the field description reads "When set, this session was revoked (idle / absolute-max / concurrent-cap / admin). System-managed." (packages/platform-objects/src/identity/sys-session.object.ts, ~lines 119–130). In practice the admin/user-initiated interactive revoke never stamps them, so the ADR-0069 D4 revoke-audit trail is effectively inert. The fields also appear in no listView.

Root cause (as located)

The four documented causes split into two paths:

  • Automatic lifecycle enforcement (idle / absolute-max / concurrent-cap): packages/plugins/plugin-auth/src/auth-manager.ts does stamp revoked_at/revoke_reason — it expires the session in place (expires_at set to the past + the two columns written) at ~lines 4606 ({ …, revoked_at, revoke_reason: reason }) and 4647 (revoke_reason: 'concurrent_cap'). So the run's "no writer anywhere" is imprecise: these three causes do have a writer. But better-auth garbage-collects expired sessions, so even these tombstones are best-effort (noted in the plugin-auth changelog).
  • Admin / user-initiated interactive revoke (revoke-user-sessions, revoke-other-sessions, single-session revoke from the Sessions list): better-auth deletes the row rather than tombstoning it, so the admin cause is never recorded — revoked_at/revoke_reason are never written for the very path an audit trail most wants.

Net: the D4 revoke-audit trail is inert/unreliable — a deleted row carries no revoke_reason, and the enforcement-stamped rows are GC'd. This is an enforce-or-remove candidate: either make revocation tombstone-in-place across all four causes (and keep the row long enough to audit), or drop the two columns and the "System-managed" claim.

Reproduction

  1. As a user with an active session, revoke-other-sessions (or admin revoke-user-sessions against a target).
  2. Read sys_session for the revoked session id → the row is gone (deleted), so revoked_at/revoke_reason were never populated for this admin revocation.
  3. (Enforcement path) trigger an absolute-max/concurrent-cap eviction → revoked_at/revoke_reason are stamped, but the expired row is subject to better-auth GC.

Source

Extracted from the QA run #7663 (framework 92f26f7, console 09987b680).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions