Documentation
¶
Overview ¶
Package session owns the cookie-based session pipeline. S02 ships the AEAD-encrypted cookie store; the Store interface lets later sprints add a server-side store for revocation without touching handlers.
Index ¶
Constants ¶
const CookieName = "_shithub_session"
CookieName is the canonical session cookie name. Prefixed with an underscore so it doesn't collide with any future user-controllable name.
const DefaultMaxAge = 30 * 24 * time.Hour
DefaultMaxAge is the default cookie lifetime.
Variables ¶
var (
ErrInvalidKey = errors.New("session: invalid key size")
)
Sentinel errors callers may check for.
Functions ¶
func GenerateKey ¶
GenerateKey returns a new random 32-byte key suitable for NewCookieStore. Used by the operator's first-run setup; production keys come from config.
Types ¶
type CookieStore ¶
type CookieStore struct {
// contains filtered or unexported fields
}
CookieStore is the AEAD-encrypted-cookie implementation.
func NewCookieStore ¶
func NewCookieStore(cfg CookieStoreConfig) (*CookieStore, error)
NewCookieStore constructs a store. Returns an error if the key is wrong size — this is a configuration error and must surface at startup.
func (*CookieStore) Clear ¶
func (c *CookieStore) Clear(w http.ResponseWriter)
Clear deletes the session cookie.
func (*CookieStore) Load ¶
func (c *CookieStore) Load(r *http.Request) (*Session, error)
Load returns the session bound to the cookie, or a fresh empty session when none is present or the cookie is invalid (expired / wrong key / tampered). Errors are surfaced for malformed input but a missing or invalid cookie yields an empty session, not an error — the caller can proceed with anonymous handling.
func (*CookieStore) Save ¶
func (c *CookieStore) Save(w http.ResponseWriter, _ *http.Request, s *Session) error
Save serializes, encrypts, and sets the session cookie.
type CookieStoreConfig ¶
type CookieStoreConfig struct {
Key []byte
MaxAge time.Duration
Secure bool
Domain string
Path string
SameSite http.SameSite
}
CookieStoreConfig holds the ergonomic options. Key MUST be 32 bytes.
type Session ¶
type Session struct {
UserID int64 `json:"uid,omitempty"`
Pre2FAUserID int64 `json:"p2,omitempty"` // set after password OK, before TOTP step
Recent2FAAt int64 `json:"r2,omitempty"` // unix-seconds of last successful 2FA challenge
// Epoch is the users.session_epoch value at issue time. The session
// loader compares it against the current DB value on every request;
// "log out everywhere" bumps the column, invalidating every cookie
// that still carries the old epoch. Zero is the unbumped baseline
// and matches the migration's column default.
Epoch int32 `json:"e,omitempty"`
CSRFToken string `json:"csrf,omitempty"`
Theme string `json:"theme,omitempty"`
Flashes []string `json:"flashes,omitempty"`
Extras map[string]string `json:"extras,omitempty"`
IssuedAt int64 `json:"iat,omitempty"`
// ImpersonatedUserID — when non-zero, the admin (UserID above) is
// currently viewing as this user. ImpersonateWriteOK gates writes
// (defaults false; admin opts in via the typed-name confirm step
// in /admin/impersonate). ImpersonationStartedAt drives the
// inactivity timeout (S34: 1 hour idle).
ImpersonatedUserID int64 `json:"imp,omitempty"`
ImpersonateWriteOK bool `json:"impw,omitempty"`
ImpersonationStartedAt int64 `json:"impt,omitempty"`
}
Session is the data carried in a cookie. The shape is intentionally small; anything that doesn't fit a few hundred bytes belongs server-side.
func (*Session) AddFlash ¶
AddFlash appends a flash message; it'll be rendered + cleared on the next page load.
func (*Session) IsAnonymous ¶
IsAnonymous returns true when no user is bound to the session.
func (*Session) PopFlashes ¶
PopFlashes returns and clears any pending flash messages.
type Store ¶
type Store interface {
Load(r *http.Request) (*Session, error)
Save(w http.ResponseWriter, r *http.Request, s *Session) error
Clear(w http.ResponseWriter)
}
Store is the abstraction every handler uses. The cookie implementation stores everything client-side; future implementations may persist server-side rows for revocation.