Help us shape the future of Go! Take the 2026 Go Developer Survey

session

package
v0.30.0 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: MIT Imports: 27 Imported by: 1

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

Examples

Constants

View Source
const DefaultTable = "sessions"

DefaultTable is the table the SQL store owns when WithTable is not used. It is listed with every other table vukem owns in docs/database.md#tables-the-framework-owns.

View Source
const MigrationID = "20261018000001_vukem_create_sessions_table"

MigrationID is the id of the migration Migrations returns; it names the framework's own migration in vukem migrate:status.

Variables

View Source
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.

View Source
var ErrListUnsupported = errors.New(
	"session: listing sessions needs a server-side ListStore (SESSION_DRIVER=database)",
)

ErrListUnsupported is returned by Manager.UserSessions when the sessions live in cookies or in a store that is not a ListStore.

View Source
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.

View Source
var ErrSessionNotFound = errors.New("session: session not found")

ErrSessionNotFound is returned by UserStore.Update when the session no longer exists.

Functions

func Migrations added in v0.29.0

func Migrations(table string) []schema.Migration

Migrations returns the migration that creates the sessions table (the default when table is empty), so an app can manage it with vukem migrate instead of leaving it to the boot-time CreateTable. The statements are the ones CreateTable runs, which stays as the fallback for apps that do not register it.

The DDL follows the dialect the migration runs on, so the same migration serves SQLite, PostgreSQL and MySQL. On a database where the table already exists (the store created it before the app opted in) the migration records itself and changes nothing; Down drops the table.

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 with the purpose vukem.New uses:
	// app.DeriveKey("SESSION_DRIVER=cookie", 32).
	CookieKey []byte
	// PreviousCookieKeys still open cookie sessions sealed before an
	// APP_KEY rotation (derive them from APP_PREVIOUS_KEYS with
	// app.DerivePreviousKeys); such a session is re-sealed with CookieKey on
	// that request. Each must be 32 bytes.
	PreviousCookieKeys [][]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
	// RecordClient stores the User-Agent (capped at 256 bytes) and the
	// client network (IPv4 /24, IPv6 /48) with each new logged-in session
	// of a ListStore, for Manager.UserSessions. Off by default: both are
	// personal data, so turn it on only with a reason to show them.
	RecordClient bool
}

Config configures a Manager. Zero values use secure defaults.

type Info added in v0.25.0

type Info struct {
	// Handle names the session for RevokeSession: the first 16 hex digits
	// of SHA-256(storage key). It is safe to show and cannot load the
	// session.
	Handle string
	// CreatedAt is when the session started (the login regenerates it).
	CreatedAt time.Time
	// LastSeenAt is the last request, to the minute (writes that only
	// refresh the idle timeout happen at most once a minute).
	LastSeenAt time.Time
	// ExpiresAt is when the session ends unless it is used again.
	ExpiresAt time.Time
	// UserAgent and IP are recorded at login only with Config.RecordClient;
	// IP keeps the network part only (IPv4 /24, IPv6 /48). UserAgent comes
	// from the client: control and format runes are removed, but escape it
	// when rendering (html/template does).
	UserAgent, IP string
	// Current is true for the session of the currentID given to
	// UserSessions.
	Current bool
}

Info describes one session of a user.

type ListStore added in v0.25.0

type ListStore interface {
	UserStore
	// ListUser returns the live sessions of userID.
	ListUser(ctx context.Context, userID string) ([]StoredSession, error)
	// DeleteUserKey deletes the session stored under key when it belongs
	// to userID and reports whether one was deleted.
	DeleteUserKey(ctx context.Context, userID, key string) (bool, error)
}

ListStore is a UserStore that lists the sessions of a user and deletes one of them by key, for an "active sessions" page. SQLStore, MemoryStore and drivers/redis implement it.

type Manager

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

Manager loads and saves sessions around each request.

func New

func New(cfg Config) (*Manager, error)

New validates cfg and returns a Manager. Insecure production settings are boot errors (docs/security.md, A02/A07).

func (*Manager) CookieName

func (m *Manager) CookieName() string

CookieName returns the effective cookie name.

func (*Manager) Cookies added in v0.5.0

func (m *Manager) Cookies(ctx context.Context, data map[string]any) []*http.Cookie

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

func (m *Manager) Middleware() func(http.Handler) http.Handler

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

func (m *Manager) Peek(ctx context.Context, cookies []*http.Cookie) map[string]any

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

func (m *Manager) RevokeOthers(ctx context.Context, userID, keepID string) error

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.

func (*Manager) RevokeSession added in v0.25.0

func (m *Manager) RevokeSession(ctx context.Context, userID, handle string) error

RevokeSession ends the session of userID named by handle (from UserSessions). It returns ErrSessionNotFound when handle names no session of that user, so one user cannot end another's sessions, and ErrRevokeUnsupported for cookie sessions and stores that are not a ListStore.

func (*Manager) UserSessions added in v0.25.0

func (m *Manager) UserSessions(ctx context.Context, userID, currentID string) ([]Info, error)

UserSessions lists the live sessions of userID (the owner recorded under web.SessionUserKey, auth.SessionGuard.SessionUserID), most recently used first; currentID marks the caller's own session. It returns ErrListUnsupported for cookie sessions and stores that are not a ListStore.

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) DeleteUserKey added in v0.25.0

func (m *MemoryStore) DeleteUserKey(_ context.Context, userID, key string) (bool, error)

DeleteUserKey implements ListStore.

func (*MemoryStore) Len

func (m *MemoryStore) Len() int

Len returns the number of stored sessions (tests).

func (*MemoryStore) ListUser added in v0.25.0

func (m *MemoryStore) ListUser(_ context.Context, userID string) ([]StoredSession, error)

ListUser implements ListStore.

func (*MemoryStore) Load

func (m *MemoryStore) Load(_ context.Context, id string) ([]byte, bool, error)

Load implements Store.

func (*MemoryStore) RevokeOthers added in v0.24.0

func (m *MemoryStore) RevokeOthers(_ context.Context, userID, keepID string) error

RevokeOthers implements UserStore.

func (*MemoryStore) Save

func (m *MemoryStore) Save(_ context.Context, id string, data []byte, expiry time.Time) error

Save implements Store; the recorded user is kept.

func (*MemoryStore) Update added in v0.24.0

func (m *MemoryStore) Update(_ context.Context, id, userID string, data []byte, expiry time.Time) error

Update implements UserStore.

type SQLOption

type SQLOption func(*SQLStore)

SQLOption configures NewSQLStore.

func WithDollarPlaceholders

func WithDollarPlaceholders() SQLOption

WithDollarPlaceholders uses $1, $2... (PostgreSQL) instead of ?.

func WithMySQL added in v0.19.0

func WithMySQL() SQLOption

WithMySQL uses MySQL/MariaDB DDL and upserts. The id column is VARBINARY so session ids compare exactly (MySQL's default collations are case-insensitive).

func WithTable

func WithTable(name string) SQLOption

WithTable sets the table name (default "sessions").

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

func NewSQLStore(db *sql.DB, opts ...SQLOption) (*SQLStore, error)

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

func (s *SQLStore) CreateTable(ctx context.Context) error

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) Delete

func (s *SQLStore) Delete(ctx context.Context, id string) error

Delete implements Store, also removing a not yet re-keyed legacy row.

func (*SQLStore) DeleteUserKey added in v0.25.0

func (s *SQLStore) DeleteUserKey(ctx context.Context, userID, key string) (bool, error)

DeleteUserKey implements ListStore.

func (*SQLStore) ListUser added in v0.25.0

func (s *SQLStore) ListUser(ctx context.Context, userID string) ([]StoredSession, error)

ListUser implements ListStore. Rows still keyed by a clear id (written before ids were hashed) are left out: their key is a session id.

func (*SQLStore) Load

func (s *SQLStore) Load(ctx context.Context, id string) ([]byte, bool, error)

Load implements Store.

func (*SQLStore) Prune

func (s *SQLStore) Prune(ctx context.Context) (int64, error)

Prune deletes expired sessions; schedule it periodically.

func (*SQLStore) RevokeOthers added in v0.24.0

func (s *SQLStore) RevokeOthers(ctx context.Context, userID, keepID string) error

RevokeOthers implements UserStore. Sessions saved before user_id existed carry no user until their next save and are not matched.

func (*SQLStore) Save

func (s *SQLStore) Save(ctx context.Context, id string, data []byte, expiry time.Time) error

Save implements Store (upsert); the recorded user is kept.

func (*SQLStore) Update added in v0.24.0

func (s *SQLStore) Update(ctx context.Context, id, userID string, data []byte, expiry time.Time) error

Update implements UserStore.

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) All

func (s *Session) All() map[string]any

All returns a copy of the user data (internal keys excluded).

func (*Session) Flash

func (s *Session) Flash(key string, value any)

Flash stores a value for the next request only.

func (*Session) Forget

func (s *Session) Forget(keys ...string)

Forget removes keys.

func (*Session) Get

func (s *Session) Get(key string) any

Get returns a value or nil.

func (*Session) GetInt64

func (s *Session) GetInt64(key string) (int64, bool)

GetInt64 returns an integer value stored by Put (JSON numbers included).

func (*Session) GetString

func (s *Session) GetString(key string) string

GetString returns a string value or "".

func (*Session) Has

func (s *Session) Has(key string) bool

Has reports whether key is set.

func (*Session) ID

func (s *Session) ID() string

ID returns the server-side session id: the store key, also listed by session devices and revocation. A new session has an id only once saved (after the response); Regenerate and Invalidate issue a new one at once. Cookie sessions (no Store) keep their data in the encrypted cookie and have no id: ID is always "" for them, Regenerate included.

func (*Session) Invalidate

func (s *Session) Invalidate()

Invalidate clears all data and issues a new id (logout).

func (*Session) Pull

func (s *Session) Pull(key string) any

Pull returns and removes a value.

func (*Session) Put

func (s *Session) Put(key string, value any)

Put stores a JSON-compatible value.

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 StoredSession added in v0.25.0

type StoredSession struct {
	// Key is hex(SHA-256(session id)), never the id itself.
	Key string
	// Data is the encoded session, as passed to Create and Update.
	Data []byte
	// Expiry is when the session ends unless it is used again.
	Expiry time.Time
}

StoredSession is one session as a ListStore returns it.

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, MemoryStore and drivers/redis implement it; cookie sessions cannot.

Directories

Path Synopsis
Package sessiontest is the conformance suite every session.ListStore passes (memory and database in core, Redis in drivers/redis).
Package sessiontest is the conformance suite every session.ListStore passes (memory and database in core, Redis in drivers/redis).

Jump to

Keyboard shortcuts

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