session

package
v1.0.126 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 18, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package session implements Culvert's HMAC-SHA256-signed session tokens and the revocation machinery behind them (ADR-0002 extraction; engine was session.go in package main).

The package owns: the signing key (with synchronized access — the key is written at runtime by the cluster snapshot sync while the MAC path reads it concurrently), token encode/decode (base64(json).HMAC), the revocation list (token- and user-level, with lazy expiry eviction, gossip export/merge, and optional disk persistence), the session TTL, and jti generation.

package main keeps: the HTTP cookie helpers (they need isSecureRequest and the Identity hub type), the startup wiring (env/config key priority — env is read in the startup shim per the slice convention), and the Session→Identity conversion (Identity is a main hub type this package must not import).

Index

Constants

View Source
const CookieName = "ps_session"

CookieName is the session cookie name.

Variables

View Source
var Revoked = NewRevocationList()

Revoked is the process-wide revocation list consulted by Decode.

Functions

func Encode

func Encode(s *Session) (string, error)

Encode serialises session data, signs it with HMAC-SHA256, and returns a cookie-safe string: base64(json).HMAC.

func HasSigningKey

func HasSigningKey() bool

HasSigningKey reports whether a key is installed.

func InitRandomKey

func InitRandomKey()

InitRandomKey installs a fresh random 32-byte key. Fails loudly: session machinery that cannot generate randomness is unsafe to keep running.

func MAC

func MAC(data string) string

MAC computes the base64url HMAC-SHA256 of data under the current signing key. Exported for tests that hand-craft legacy-shaped tokens.

func NewJti

func NewJti() string

NewJti returns a fresh 128-bit random session identifier, hex encoded. Stamped into every newly-issued Session by the cookie issuers so two same-second logins for the same user produce distinct b64 payloads (and therefore distinct HMACs and cookie values). The only collision space is 2^128, far beyond birthday-bound concerns for any plausible deployment.

crypto/rand failure is treated the same as InitRandomKey's: fail loudly.

func RevocationsPath

func RevocationsPath() string

RevocationsPath returns the configured persistence path ("" = disabled).

func SetRevocationsPath

func SetRevocationsPath(p string)

SetRevocationsPath configures where SaveRevocations/LoadRevocations persist.

func SetSigningKey

func SetSigningKey(key []byte)

SetSigningKey installs the HMAC key. Callers own validation (length/hex); the cluster snapshot path and the startup shim both funnel through here. Synchronized: the DP config-sync path replaces the key at runtime while concurrent requests compute MACs (previously an unguarded global write — latent data race, fixed with this extraction).

func SetTTL

func SetTTL(d time.Duration)

SetTTL updates the session lifetime. Clamped to [15min, 7d].

func SigningKey

func SigningKey() []byte

SigningKey returns a copy of the current HMAC key (nil when unset). Used by the CP snapshot export (hex-encoded into ConfigSnapshot.SessionHMAC) and by test snapshot/restore helpers.

func TTL

func TTL() time.Duration

TTL returns the current session lifetime.

Types

type RevocationEntry

type RevocationEntry struct {
	Token  string `json:"token"`
	Expiry int64  `json:"expiry"` // Unix timestamp
}

RevocationEntry is a single revoked session token for gRPC gossip.

type RevocationList

type RevocationList struct {
	// contains filtered or unexported fields
}

RevocationList tracks revoked session tokens and user-level revocations.

func NewRevocationList

func NewRevocationList() *RevocationList

NewRevocationList returns an empty list (used by tests to swap the package singleton, mirroring the bl pointer-swap idiom).

func (*RevocationList) Count

func (r *RevocationList) Count() int

Count returns the number of active revoked sessions.

func (*RevocationList) ExportRevocations

func (r *RevocationList) ExportRevocations() []RevocationEntry

ExportRevocations returns all non-expired revocation entries for syncing.

func (*RevocationList) IsRevoked

func (r *RevocationList) IsRevoked(token string) bool

IsRevoked reports whether the token is currently revoked, lazily evicting entries whose original expiry has passed.

func (*RevocationList) IsUserRevoked

func (r *RevocationList) IsUserRevoked(username string) bool

IsUserRevoked returns true if all sessions for the given username are revoked.

func (*RevocationList) LoadRevocations

func (r *RevocationList) LoadRevocations() error

LoadRevocations reads revocations from disk and merges them.

func (*RevocationList) MergeRevocations

func (r *RevocationList) MergeRevocations(entries []RevocationEntry) int

MergeRevocations imports remote revocation entries (from other cluster nodes). Only adds entries that are not yet expired and not already present.

func (*RevocationList) Revoke

func (r *RevocationList) Revoke(token string, exp time.Time)

Revoke marks a token (its b64 payload) as revoked until exp.

func (*RevocationList) RevokeUser

func (r *RevocationList) RevokeUser(username string)

RevokeUser invalidates all sessions for a username. Active session cookies for this user are rejected until the revocation expiry (max session TTL). Called when a user account is deleted (Finding 5.2).

func (*RevocationList) SaveRevocations

func (r *RevocationList) SaveRevocations() error

SaveRevocations writes all non-expired revocations to disk as JSON.

func (*RevocationList) SwapForTest

func (r *RevocationList) SwapForTest() (restore func())

SwapForTest replaces the list's maps with empty ones and returns a restore function. Mirrors the snapshot/restore pattern documented in CLAUDE.md for tests that touch shared revocation state.

type Session

type Session struct {
	Sub      string   `json:"sub"`
	Email    string   `json:"email"`
	Name     string   `json:"name"`
	Groups   []string `json:"grp,omitempty"`
	Provider string   `json:"pvd"`
	Role     string   `json:"role,omitempty"` // UI admin role: admin|operator|viewer
	Exp      int64    `json:"exp"`            // Unix timestamp
	// Jti is a 128-bit random session identifier (hex-encoded). Added in
	// Phase C5.1 to make the JSON payload unique per login even when two
	// logins for the same user happen in the same wall-clock second —
	// without Jti the (Sub, Role, Exp-in-seconds) tuple yielded a
	// byte-identical payload, identical b64, identical HMAC, and an
	// effectively-revoked cookie if any prior login of that tuple had
	// been revoked. omitempty keeps legacy cookies (issued before C5.1)
	// decoding cleanly: their Jti unmarshals to "" and the field is
	// simply absent on the wire.
	Jti string `json:"jti,omitempty"`
}

Session is the payload stored inside the signed proxy session cookie. It carries just enough identity data to reconstruct an Identity object without talking to the IdP on every request. (The Session→Identity conversion lives in package main — Identity is a main hub type.)

func Decode

func Decode(raw string) (*Session, error)

Decode parses and verifies a session cookie value. Returns an error when the signature is invalid, the session has expired, or the token/user has been revoked.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL