KemalIdentity::Sessions::Repository
Where sessions live.
The hot path is #find_by_digest, and it is the one method whose cost shows up on every
authenticated request. Everything else runs at login, at logout, or in a background
sweep.
Concurrency
Implementations must be safe for concurrent use from multiple fibers on multiple threads
(docs/01-architecture.md). Two simultaneous logins for one account must produce two
distinct sessions, and #create must refuse a duplicate digest rather than overwrite —
see below.
Instance methods
Stores a new session.
Raises KemalIdentity::InfrastructureError if record.token_digest is already present.
This is not defensive noise: auth_sessions.token_digest carries a unique index
precisely so that a digest collision is a loud database error rather than a silent
security failure in which two accounts share a session
(docs/03-data-model.md). An implementation that upserted here would convert that
error into exactly the failure the index exists to prevent.
Deletes rows whose absolute_expires_at is at or before before, returning the count.
Disk reclamation only. Correctness never depends on this having run: expiry is
evaluated on every read, which is the direct lesson of kemal-session issue #116, where
a timeout only marked a session for deletion at the next GC pass and a read could
refresh its access time before any expiry check, reviving it
(docs/02-security-model.md).
Deletes revoked rows whose revoked_at is at or before before, returning the count.
Separate from #delete_expired because a revoked session is not necessarily an expired
one: logging out at nine in the morning revokes a row whose absolute deadline is still
hours away, and that row is worth keeping for a while. It is the evidence behind "you
were signed out of this device", and deleting it the instant it is revoked throws away
the only record that the logout happened.
The retention window is the application's to choose. Disk reclamation either way — correctness never depends on this having run, because revocation is evaluated on read.
Resolves a session by the digest of its token, returning session state and account status together.
Returns nil — never raises — when nothing matches. "Unknown digest" is the ordinary
case for an expired cookie, a tampered one, or one from a previous deployment.
Also returns nil when the session's account does not exist. The reference SQL is an
inner join, so a session pointing at a deleted account resolves to nothing: the
failure mode is closed, not open.
Revocation, expiry and account status are not evaluated here. This method reports
facts; SessionService decides what they mean. That split is what lets the same
repository serve a "list my devices" screen that wants to see revoked rows.
Marks one session revoked, returning false if it does not exist or was already revoked.
Already-revoked returns false rather than raising: logging out twice is not an error, and the caller learns whether it changed anything.
Revokes every live session for an account, optionally sparing one, and returns how many it revoked.
except_id is what makes "log out everywhere else" and "change password without
logging myself out" possible. Already-revoked sessions are not counted and not
re-stamped, so the count is the number of sessions actually ended.