Documentation
¶
Overview ¶
Package session provides server-side and cookie sessions with flash messages, old input, regeneration (anti-fixation) and idle/absolute timeouts. Sessions are loaded lazily and written only when modified.
A Manager built by New wraps handlers with Manager.Middleware; handlers reach the Session through web.Ctx.Session. Data lives in an AES-GCM encrypted cookie by default, or server-side in a Store: MemoryStore, SQLStore or drivers/redis. Apps get a manager from app.Sessions(), configured by SESSION_* variables.
See https://github.com/AlexGalhardo/vukem/blob/main/docs/http.md.
Example ¶
package main
import (
"fmt"
"log/slog"
"net/http"
"net/http/httptest"
"github.com/AlexGalhardo/vukem/session"
"github.com/AlexGalhardo/vukem/web"
)
func main() {
m, err := session.New(session.Config{
Store: session.NewMemoryStore(), // nil = encrypted cookie sessions (CookieKey)
Log: slog.New(slog.DiscardHandler),
})
if err != nil {
fmt.Println(err)
return
}
visits := m.Middleware()(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
s, _ := web.SessionFrom(r.Context()).(*session.Session)
n, _ := s.GetInt64("visits")
s.Put("visits", n+1)
fmt.Fprintf(w, "visit %d", n+1)
}))
var cookies []*http.Cookie
for range 2 {
req := httptest.NewRequest(http.MethodGet, "/", http.NoBody)
for _, ck := range cookies {
req.AddCookie(ck)
}
rec := httptest.NewRecorder()
visits.ServeHTTP(rec, req)
cookies = rec.Result().Cookies()
fmt.Println(rec.Body.String())
}
}
Output: visit 1 visit 2
Index ¶
- Variables
- type Config
- type Manager
- func (m *Manager) CookieName() string
- func (m *Manager) Cookies(ctx context.Context, data map[string]any) []*http.Cookie
- func (m *Manager) Middleware() func(http.Handler) http.Handler
- func (m *Manager) Peek(ctx context.Context, cookies []*http.Cookie) map[string]any
- func (m *Manager) RevokeOthers(ctx context.Context, userID, keepID string) error
- type MemoryStore
- func (m *MemoryStore) Create(_ context.Context, id, userID string, data []byte, expiry time.Time) error
- func (m *MemoryStore) Delete(_ context.Context, id string) error
- func (m *MemoryStore) Len() int
- func (m *MemoryStore) Load(_ context.Context, id string) ([]byte, bool, error)
- func (m *MemoryStore) RevokeOthers(_ context.Context, userID, keepID string) error
- func (m *MemoryStore) Save(_ context.Context, id string, data []byte, expiry time.Time) error
- func (m *MemoryStore) Update(_ context.Context, id, userID string, data []byte, expiry time.Time) error
- type SQLOption
- type SQLStore
- func (s *SQLStore) Create(ctx context.Context, id, userID string, data []byte, expiry time.Time) error
- func (s *SQLStore) CreateTable(ctx context.Context) error
- func (s *SQLStore) Delete(ctx context.Context, id string) error
- func (s *SQLStore) Load(ctx context.Context, id string) ([]byte, bool, error)
- func (s *SQLStore) Prune(ctx context.Context) (int64, error)
- func (s *SQLStore) RevokeOthers(ctx context.Context, userID, keepID string) error
- func (s *SQLStore) Save(ctx context.Context, id string, data []byte, expiry time.Time) error
- func (s *SQLStore) Update(ctx context.Context, id, userID string, data []byte, expiry time.Time) error
- type Session
- func (s *Session) All() map[string]any
- func (s *Session) Flash(key string, value any)
- func (s *Session) Forget(keys ...string)
- func (s *Session) Get(key string) any
- func (s *Session) GetInt64(key string) (int64, bool)
- func (s *Session) GetString(key string) string
- func (s *Session) Has(key string) bool
- func (s *Session) ID() string
- func (s *Session) Invalidate()
- func (s *Session) Pull(key string) any
- func (s *Session) Put(key string, value any)
- func (s *Session) Reflash()
- func (s *Session) Regenerate()
- type Store
- type UserStore
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrCookieTooLarge = errors.New("session: cookie session data exceeds 4 KB; use a server-side store")
ErrCookieTooLarge is returned when cookie session data exceeds 4 KB.
var ErrRevokeUnsupported = errors.New(
"session: revoking sessions needs a server-side UserStore (SESSION_DRIVER=database)",
)
ErrRevokeUnsupported is returned by Manager.RevokeOthers when the sessions live in cookies or in a store that is not a UserStore: there is no server-side record to delete.
var ErrSessionNotFound = errors.New("session: session not found")
ErrSessionNotFound is returned by UserStore.Update when the session no longer exists.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// CookieName defaults to "vukem_session"; with Secure, Path "/" and no
// Domain the "__Host-" prefix is added automatically.
CookieName string
// Path defaults to "/".
Path string
// Domain is empty (host-only cookie) by default.
Domain string
// Secure sends the cookie over HTTPS only. Required in production.
Secure bool
// SameSite defaults to Lax.
SameSite http.SameSite
// IdleTimeout ends sessions after inactivity (default 2h).
IdleTimeout time.Duration
// Lifetime ends sessions this long after creation (default 24h).
Lifetime time.Duration
// Store keeps sessions server-side; nil stores them in an encrypted
// cookie (CookieKey required).
Store Store
// CookieKey is the 32-byte AES-256-GCM key for cookie sessions; derive
// it from APP_KEY: app.Key.Derive("session.cookie", 32).
CookieKey []byte
// Production enables boot-time checks (Secure must be true).
Production bool
// Now is the clock (tests); defaults to time.Now. When set, New hands
// it to the store so expiry and Prune follow the same clock: the
// built-in MemoryStore and SQLStore, and any store (or wrapper
// embedding one) with a setClock(func() time.Time) method.
Now func() time.Time
// Log receives security events and storage errors.
Log *slog.Logger
}
Config configures a Manager. Zero values use secure defaults.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager loads and saves sessions around each request.
func New ¶
New validates cfg and returns a Manager. Insecure production settings are boot errors (docs/security.md, A02/A07).
func (*Manager) CookieName ¶
CookieName returns the effective cookie name.
func (*Manager) Cookies ¶ added in v0.5.0
Cookies returns the cookies of a new session holding data, as the middleware would set them. Tests use it to start a request already logged in (vukemtest Client.ActingAs).
func (*Manager) Middleware ¶
Middleware loads the session lazily, exposes it through web.Ctx (and web.SessionFrom), installs the flash-and-redirect validation hook, and saves it before the response headers are sent.
func (*Manager) Peek ¶
Peek decodes the session carried by cookies, as the next request would see it. Tests use it to assert on flash data and session values.
func (*Manager) RevokeOthers ¶ added in v0.24.0
RevokeOthers ends every session of userID except keepID (the current session id, or "" to end them all): call it after a password change or reset and when two-factor authentication is turned on. Sessions belong to the user stored under web.SessionUserKey, which auth guards set on login. It returns ErrRevokeUnsupported for cookie sessions and stores that are not a UserStore.
type MemoryStore ¶
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore keeps sessions in process memory (development and tests).
func NewMemoryStore ¶
func NewMemoryStore() *MemoryStore
NewMemoryStore returns an empty MemoryStore.
func (*MemoryStore) Create ¶ added in v0.24.0
func (m *MemoryStore) Create(_ context.Context, id, userID string, data []byte, expiry time.Time) error
Create implements UserStore.
func (*MemoryStore) Delete ¶
func (m *MemoryStore) Delete(_ context.Context, id string) error
Delete implements Store.
func (*MemoryStore) Len ¶
func (m *MemoryStore) Len() int
Len returns the number of stored sessions (tests).
func (*MemoryStore) RevokeOthers ¶ added in v0.24.0
func (m *MemoryStore) RevokeOthers(_ context.Context, userID, keepID string) error
RevokeOthers implements UserStore.
type SQLOption ¶
type SQLOption func(*SQLStore)
SQLOption configures NewSQLStore.
func WithDollarPlaceholders ¶
func WithDollarPlaceholders() SQLOption
WithDollarPlaceholders uses $1, $2... (PostgreSQL) instead of ?.
type SQLStore ¶
type SQLStore struct {
// contains filtered or unexported fields
}
SQLStore keeps sessions in a SQL table (SQLite, PostgreSQL or MySQL):
CREATE TABLE sessions (id TEXT PRIMARY KEY, data TEXT NOT NULL, expires_at BIGINT NOT NULL, user_id VARCHAR(255))
The id column holds hex(SHA-256(session id)), never the cookie value, so reading the table (backup, SQL injection elsewhere, replica access) does not yield session cookies. Rows written before ids were hashed are re-keyed on their first load and otherwise expire as usual.
func NewSQLStore ¶
NewSQLStore returns a SQLStore. The table name is validated because it is interpolated into SQL (A05).
func (*SQLStore) Create ¶ added in v0.24.0
func (s *SQLStore) Create(ctx context.Context, id, userID string, data []byte, expiry time.Time) error
Create implements UserStore.
func (*SQLStore) CreateTable ¶
CreateTable creates the sessions table if it does not exist, and adds the user_id column and its index to tables created by older versions. It is idempotent and safe to run from several servers at boot.
func (*SQLStore) RevokeOthers ¶ added in v0.24.0
RevokeOthers implements UserStore. Sessions saved before user_id existed carry no user until their next save and are not matched.
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session is one user's session. It is safe for concurrent use within a request.
func (*Session) Invalidate ¶
func (s *Session) Invalidate()
Invalidate clears all data and issues a new id (logout).
func (*Session) Reflash ¶
func (s *Session) Reflash()
Reflash keeps the current flash data for one more request.
func (*Session) Regenerate ¶
func (s *Session) Regenerate()
Regenerate issues a new id and keeps the data. Call it after login and any privilege change (session fixation, A07).
type Store ¶
type Store interface {
// Load returns the data saved for id; found is false when the session
// does not exist or expired.
Load(ctx context.Context, id string) (data []byte, found bool, err error)
// Save stores data for id until expiry.
Save(ctx context.Context, id string, data []byte, expiry time.Time) error
// Delete removes id.
Delete(ctx context.Context, id string) error
}
Store persists server-side sessions.
type UserStore ¶ added in v0.24.0
type UserStore interface {
Store
// Create stores a new session owned by userID ("" for a guest).
Create(ctx context.Context, id, userID string, data []byte, expiry time.Time) error
// Update rewrites an existing session and returns ErrSessionNotFound
// when it is gone (revoked, logged out, pruned): a request that was in
// flight during a revocation must not write the session back.
Update(ctx context.Context, id, userID string, data []byte, expiry time.Time) error
// RevokeOthers deletes every session of userID except keepID ("" keeps
// none).
RevokeOthers(ctx context.Context, userID, keepID string) error
}
UserStore is a Store that records which user owns each session, so all sessions of a user can be ended at once (password reset, 2FA enable). SQLStore and MemoryStore implement it; cookie sessions cannot.