Documentation
¶
Overview ¶
Package session provides Redis-backed session persistence and compact binary session encoding for authentication hot paths.
Binary encoding ¶
Sessions are stored in Redis as a compact binary format (schema versions v1–v5) with forward migration on read. The encoder is append-only: new versions add fields but never reinterpret old ones.
Architecture boundaries ¶
This package owns the Store (Redis operations) and the Session model. It does NOT interpret JWT tokens, evaluate permissions, or enforce authentication policy — those responsibilities belong to the Engine.
What this package must NOT do ¶
- Import goAuth, jwt, or permission (no upward imports).
- Perform application-level authorization decisions.
- Store plaintext secrets in Session fields.
Index ¶
- Constants
- Variables
- func Encode(s *Session) ([]byte, error)
- type Session
- type Store
- func (s *Store) ActiveSessionCount(ctx context.Context, tenantID, userID string) (int, error)
- func (s *Store) ActiveSessionIDs(ctx context.Context, tenantID, userID string) ([]string, error)
- func (s *Store) Delete(ctx context.Context, tenantID, sessionID string) error
- func (s *Store) DeleteAllForUser(ctx context.Context, tenantID, userID string) error
- func (s *Store) EstimateActiveSessions(ctx context.Context, tenantID string) (int, error)
- func (s *Store) Get(ctx context.Context, tenantID, sessionID string, ttl time.Duration) (*Session, error)
- func (s *Store) GetManyReadOnly(ctx context.Context, tenantID string, sessionIDs []string) ([]*Session, error)
- func (s *Store) GetReadOnly(ctx context.Context, tenantID, sessionID string) (*Session, error)
- func (s *Store) Ping(ctx context.Context) (time.Duration, error)
- func (s *Store) RotateRefreshHash(ctx context.Context, tenantID, sessionID string, providedHash [32]byte, ...) (*Session, error)
- func (s *Store) Save(ctx context.Context, sess *Session, ttl time.Duration) error
- func (s *Store) SetTenantSessionCount(ctx context.Context, tenantID string, count int) error
- func (s *Store) ShouldEmitDeviceAnomaly(ctx context.Context, sessionID, kind string, window time.Duration) (bool, error)
- func (s *Store) TenantSessionCount(ctx context.Context, tenantID string) (int, error)
- func (s *Store) TrackReplayAnomaly(ctx context.Context, sessionID string, ttl time.Duration) error
Constants ¶
const (
// CurrentSchemaVersion is the currently encoded Redis session schema version.
CurrentSchemaVersion = 5
)
Variables ¶
ErrRedisUnavailable is an exported constant or variable used by the authentication engine.
var ErrRefreshHashMismatch = errors.New("refresh hash mismatch")
ErrRefreshHashMismatch is an exported constant or variable used by the authentication engine.
var ErrRefreshSessionCorrupt = errors.New("refresh session corrupt")
ErrRefreshSessionCorrupt is returned when the refresh target session blob is invalid.
var ErrRefreshSessionExpired = errors.New("refresh session expired")
ErrRefreshSessionExpired is returned when the refresh target session is expired.
var ErrRefreshSessionNotFound = errors.New("refresh session not found")
ErrRefreshSessionNotFound is returned when the refresh target session does not exist.
Functions ¶
Types ¶
type Session ¶
type Session struct {
// SchemaVersion is the on-wire session schema version decoded from Redis.
// New writes always encode with CurrentSchemaVersion.
SchemaVersion uint8
SessionID string
UserID string
TenantID string
Role string
Mask interface{}
PermissionVersion uint32
RoleVersion uint32
AccountVersion uint32
Status uint8
RefreshHash [32]byte
IPHash [32]byte
UserAgentHash [32]byte
CreatedAt int64
ExpiresAt int64
}
Session is the in-memory representation of a user session. It is serialized to Redis using the v5 binary wire format via Encode/Decode.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is a Redis-backed session store that handles persistence, expiration, sliding window renewal, and atomic refresh-token rotation.
Docs: docs/session.md
func NewStore ¶
func NewStore( redis redis.UniversalClient, prefix string, sliding bool, jitterEnabled bool, jitterRange time.Duration, ) *Store
NewStore creates a session Store backed by the given Redis client. prefix sets the Redis key namespace; slidingExp, jitterEnabled, and jitterRange control expiration behavior.
Docs: docs/session.md
func (*Store) ActiveSessionCount ¶
ActiveSessionCount returns the number of tracked session IDs for a user in a tenant.
func (*Store) ActiveSessionIDs ¶
ActiveSessionIDs returns tracked session IDs for a user in a tenant.
func (*Store) Delete ¶
Delete removes a session from Redis and decrements the session counter.
Performance: 2–3 Redis commands (DEL + counter decrement). Docs: docs/session.md
func (*Store) DeleteAllForUser ¶
DeleteAllForUser removes all sessions for a user within a tenant.
ATOMICITY NOTE: This operation is NOT fully atomic. It reads the user's session set (SMembers), checks which sessions still exist (pipeline EXISTS), then deletes them (TxPipelined DEL). A session created between the read and delete phases will not be captured by this call. In practice this race is extremely narrow and only affects logout-all semantics — the stray session will expire naturally or be caught by the next DeleteAllForUser call. Callers requiring stronger guarantees can follow up with a counter reconciliation or a second DeleteAllForUser invocation.
DeleteAllForUser may return an error when input validation, dependency calls, or security checks fail. DeleteAllForUser does not mutate shared global state and can be used concurrently when the receiver and dependencies are concurrently safe.
func (*Store) EstimateActiveSessions ¶
EstimateActiveSessions scans tenant session keys and counts matches. This is an admin-only O(n) operation and must not be used in request hot paths.
func (*Store) Get ¶
func (s *Store) Get(ctx context.Context, tenantID, sessionID string, ttl time.Duration) (*Session, error)
Get retrieves a session by tenant and session ID. Returns the decoded Session or an error if not found or Redis is unavailable.
Performance: 1 Redis GET. Docs: docs/session.md
func (*Store) GetManyReadOnly ¶
func (s *Store) GetManyReadOnly(ctx context.Context, tenantID string, sessionIDs []string) ([]*Session, error)
GetManyReadOnly fetches multiple sessions without mutating Redis state.
func (*Store) GetReadOnly ¶
GetReadOnly fetches a session without mutating TTL, index, or any Redis state.
func (*Store) RotateRefreshHash ¶
func (s *Store) RotateRefreshHash( ctx context.Context, tenantID, sessionID string, providedHash [32]byte, nextHash [32]byte, ) (*Session, error)
RotateRefreshHash atomically replaces the refresh-token hash in the session using a Lua CAS script. This is the core of the rotation protocol that enables reuse detection.
Performance: 1 Lua EVALSHA (atomic compare-and-swap). Docs: docs/session.md, docs/flows.md#refresh-token-rotation Security: CAS prevents lost updates under concurrency.
func (*Store) Save ¶
Save persists a Session to Redis with the given TTL.
Performance: 2–3 Redis commands (SET + counter increment). Docs: docs/session.md
func (*Store) SetTenantSessionCount ¶
SetTenantSessionCount sets (or clears) the tracked tenant session counter.
func (*Store) ShouldEmitDeviceAnomaly ¶
func (s *Store) ShouldEmitDeviceAnomaly(ctx context.Context, sessionID, kind string, window time.Duration) (bool, error)
ShouldEmitDeviceAnomaly returns true only for the first anomaly in the window per session/kind.
func (*Store) TenantSessionCount ¶
TenantSessionCount returns the tracked tenant-wide session counter.