Documentation
¶
Overview ¶
Package seshcookie enables you to associate session-state with HTTP requests while keeping your server stateless. Because session-state is transferred as part of the HTTP request (in a cookie), state can be seamlessly maintained between server restarts or load balancing. It's inspired by Beaker (http://pypi.python.org/pypi/Beaker), which provides a similar service for Python webapps. The cookies are authenticated and encrypted (using AES-GCM) with a key derived using Argon2id from a string provided to the NewHandler function. This makes seshcookie reliable and secure: session contents are opaque to users and not able to be manipulated or forged by third parties.
Version 3.0 - Go Module v3 ¶
Version 3.0 updates the module path to follow Go's semantic import versioning (v3). Version 2.0/3.0 introduces a new API based on Protocol Buffers and Go generics. Session data is now strongly-typed using protobuf messages, providing better type safety and schema evolution. The library uses an envelope pattern where metadata (like issue time) is stored separately from the user's session payload.
Sessions have server-side expiry enforcement based on issue time, preventing cookie manipulation to extend session lifetime.
Basic Usage ¶
Define your session data as a protobuf message:
syntax = "proto3";
package myapp;
message UserSession {
string username = 1;
int64 login_time = 2;
repeated string roles = 3;
}
Then use seshcookie with Go generics:
package main
import (
"net/http"
"log"
"time"
"github.com/bpowers/seshcookie/v3"
"myapp/pb" // your generated protobuf package
)
type VisitedHandler struct{}
func (h *VisitedHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if req.URL.Path != "/" {
return
}
// GetSession returns a valid protobuf message, never nil
session, err := seshcookie.GetSession[*pb.UserSession](req.Context())
if err != nil {
http.Error(rw, "Internal error", 500)
return
}
// Modify the session
session.Username = "alice"
session.LoginTime = time.Now().Unix()
// Explicitly save changes
if err := seshcookie.SetSession(req.Context(), session); err != nil {
http.Error(rw, "Internal error", 500)
return
}
rw.Header().Set("Content-Type", "text/plain")
rw.WriteHeader(200)
rw.Write([]byte("Welcome " + session.Username))
}
func main() {
key := "session key, preferably a sequence of data from /dev/urandom"
// NewHandler now requires a type parameter
handler, err := seshcookie.NewHandler[*pb.UserSession](
&VisitedHandler{},
key,
&seshcookie.Config{
HTTPOnly: true,
Secure: true,
MaxAge: 24 * time.Hour, // Server-side expiry
})
if err != nil {
log.Fatalf("NewHandler: %s", err)
}
if err := http.ListenAndServe(":8080", handler); err != nil {
log.Fatalf("ListenAndServe: %s", err)
}
}
Session Management ¶
The API provides three main functions:
- GetSession[T](ctx) - Retrieves session from context, auto-creates if empty
- SetSession[T](ctx, session) - Marks session as changed for writing to cookie
- ClearSession[T](ctx) - Clears session, causing cookie deletion
Sessions are only written to cookies when SetSession is called, preventing unnecessary cookie updates and preserving the original issue timestamp.
Security Features ¶
- Argon2id key derivation (memory-hard, GPU-resistant)
- AES-GCM authenticated encryption
- Server-side session expiry based on issue time
- HTTPOnly and Secure cookie flags
- Automatic nonce generation for each cookie
- Change detection to minimize cookie writes
- Type-safe session data via protobuf
Migration from v1.x ¶
Version 2.0 is a breaking change that replaces the map[string]interface{} session type with strongly-typed protobuf messages. The API surface has changed significantly:
v1.x:
session := seshcookie.GetSession(ctx) session["count"] = 1
v2.x:
session, err := seshcookie.GetSession[*MyProto](ctx) session.Count = 1 seshcookie.SetSession(ctx, session)
Index ¶
- Variables
- func ClearSession[T proto.Message](ctx context.Context) error
- func GetSession[T proto.Message](ctx context.Context) (T, error)
- func NewMiddleware[T proto.Message](key string, config *Config) (func(http.Handler) http.Handler, error)
- func SetSession[T proto.Message](ctx context.Context, session T) error
- type Config
- type Handler
Constants ¶
This section is empty.
Variables ¶
var ( // DefaultConfig is used as the configuration if a nil config // is passed to NewHandler DefaultConfig = &Config{ CookieName: defaultCookieName, CookiePath: "/", HTTPOnly: true, Secure: true, MaxAge: 24 * time.Hour, } // ErrSessionExpired is returned when a session has expired ErrSessionExpired = errors.New("session expired") // ErrNoSession is returned when no session is present in the context ErrNoSession = errors.New("no session in context") // ErrTypeMismatch is returned when the session type doesn't match expected type ErrTypeMismatch = errors.New("session type mismatch") )
Functions ¶
func ClearSession ¶
ClearSession clears the session from the context. This will cause the cookie to be deleted on the next response.
func GetSession ¶
GetSession retrieves the session from the context. Returns ErrNoSession if no session context is present. If the session is empty (no cookie was present), returns a new zero instance. The returned session is always a valid proto.Message that can be modified.
func NewMiddleware ¶
func NewMiddleware[T proto.Message](key string, config *Config) (func(http.Handler) http.Handler, error)
NewMiddleware returns a middleware constructor for a new seshcookie Handler with a given encryption key and configuration. The type parameter T specifies the protobuf message type to use for sessions.
key must be non-empty and is used to derive the encryption key. config can be nil, in which case DefaultConfig is used.
Example:
mw, err := seshcookie.NewHandler[*UserSession]("my-secret-key", nil)
if err != nil {
log.Fatal(err)
}
http.Handle("/", mw(http.HandlerFunc(myHandler))
Types ¶
type Config ¶
type Config struct {
CookieName string // name of the cookie to store our session in
CookiePath string // resource path the cookie is valid for
HTTPOnly bool // don't allow JavaScript to access cookie
Secure bool // only send session over HTTPS
MaxAge time.Duration // server-side session expiry duration
}
Config provides directives to a seshcookie instance on cookie attributes, like if they are accessible from JavaScript and/or only set on HTTPS connections.
type Handler ¶
type Handler[T proto.Message] struct { http.Handler Config Config // contains filtered or unexported fields }
Handler is the seshcookie HTTP handler that provides a Session object to child handlers. It uses Go generics to provide type-safe session access.
func NewHandler ¶
func NewHandler[T proto.Message](handler http.Handler, key string, config *Config) (*Handler[T], error)
NewHandler returns a new seshcookie Handler with a given inner handler, encryption key, and configuration. The type parameter T specifies the protobuf message type to use for sessions.
key must be non-empty and is used to derive the encryption key. config can be nil, in which case DefaultConfig is used.
Example:
handler, err := seshcookie.NewHandler[*UserSession](innerHandler, "my-secret-key", nil)
if err != nil {
log.Fatal(err)
}
http.ListenAndServe(":8080", handler)