Documentation
¶
Overview ¶
Package session provides secure, signed cookie sessions for browser apps, standard library only.
The MVP is cookie-only: the whole session lives in an HMAC-SHA256 signed cookie, so there is no server-side store to run. It is thin and cryptographically conservative, and is a companion to token-based auth, not a replacement: session owns cookie and browser state, while an auth package owns subjects, passwords and tokens.
Manager ¶
m := session.New(secret,
session.WithName("sid"),
session.WithSecure(true), // HTTPS only in production
session.WithSameSite(http.SameSiteLaxMode),
session.WithTTL(24*time.Hour),
)
Reading and writing ¶
s := m.LoadOrNew(r) // existing session, or a fresh one
s.Set("theme", "dark")
err := m.Save(w, s) // sign and set the cookie
m.Destroy(w) // clear the cookie on logout
Load returns ErrNoSession when there is no cookie; LoadOrNew handles that for the common "start a session in this handler" case. With the middleware, handlers read the session from the context and persist changes explicitly:
http.Handle("/", m.Middleware(handler))
// inside handler:
s, _ := session.From(r.Context())
s.Set("seen", "1")
_ = m.Save(w, s)
Security ¶
The cookie is HttpOnly, SameSite=Lax and signed with HMAC-SHA256. Enable Secure in production. The payload is versioned so the format can evolve without breaking old cookies. Key rotation is built in: the primary key signs and any configured key (WithKey) verifies, so you can roll keys without invalidating live sessions.
Scope ¶
The MVP does not include a server-side store, encrypted payloads or CSRF; those are planned. It does not do user management, password hashing or OAuth.
See DOC.md (English) and DOC.UK.md (Ukrainian) for the full reference.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNoSession is returned when a request carries no session cookie. ErrNoSession = errors.New("session: no session cookie") // ErrInvalid is returned when a session cookie is malformed or fails its // signature check (tampering). ErrInvalid = errors.New("session: invalid session cookie") // ErrExpired is returned when a session is past its expiry. ErrExpired = errors.New("session: session expired") // ErrTooLarge is returned when an encoded session exceeds the cookie size // limit (about 4 KB). ErrTooLarge = errors.New("session: encoded session too large for a cookie") )
Functions ¶
This section is empty.
Types ¶
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager signs, reads and writes session cookies. The MVP is cookie-only: the whole session lives in a signed cookie. It is safe for concurrent use.
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"time"
"github.com/goloop/session"
)
func main() {
m := session.New([]byte("0123456789abcdef0123456789abcdef"),
session.WithName("sid"),
session.WithSecure(true),
session.WithTTL(24*time.Hour),
)
// Save a session.
rec := httptest.NewRecorder()
s := &session.Session{Subject: "user-1"}
s.Set("theme", "dark")
_ = m.Save(rec, s)
// Load it back on the next request.
req := httptest.NewRequest(http.MethodGet, "/", nil)
for _, c := range rec.Result().Cookies() {
req.AddCookie(c)
}
loaded, _ := m.Load(req)
fmt.Println(loaded.Subject, loaded.Get("theme"))
}
Output: user-1 dark
func New ¶
New creates a Manager with the given signing secret and options. The Secure cookie attribute defaults to true; disable it with WithSecure(false) only for local HTTP development. New panics if any signing key is shorter than 32 bytes: a weak key is a configuration error that must fail at startup.
func (*Manager) Destroy ¶
func (m *Manager) Destroy(w http.ResponseWriter)
Destroy clears the session cookie in the response. Because the session is a stateless signed cookie, Destroy cannot invalidate a copy the client already holds: a captured cookie stays valid until its ExpiresAt. Keep the TTL short, and for hard revocation pair this with a server-side denylist of session IDs.
func (*Manager) LoadOrNew ¶
LoadOrNew reads the session cookie, or returns a fresh empty session when there is none or it is invalid. It is the discoverable way to start a session in a handler that does not use Middleware - for example a login endpoint that sets the cookie:
s := m.LoadOrNew(r) s.Subject = userID m.Save(w, s)
func (*Manager) Middleware ¶
Middleware loads the session into the request context, creating a fresh empty session when there is none or it is invalid. Handlers read it with From and persist changes with Save.
type Option ¶
type Option func(*Manager)
Option configures a Manager.
func WithDomain ¶
WithDomain sets the cookie Domain attribute.
func WithKey ¶
WithKey adds another key for verification, enabling key rotation: the primary key (passed to New) signs, any configured key verifies.
func WithSameSite ¶
WithSameSite sets the SameSite attribute (default Lax).
func WithSecure ¶
WithSecure sets the Secure attribute (send only over HTTPS). It defaults to true; pass false only for local HTTP development.
type Session ¶
type Session struct {
ID string `json:"id"`
Subject string `json:"sub,omitempty"`
Values map[string]string `json:"val,omitempty"`
CreatedAt time.Time `json:"iat"`
ExpiresAt time.Time `json:"exp"`
}
Session is the state carried in a signed cookie. Values holds arbitrary string key/value pairs; Subject is a convenience field for the authenticated principal (it may be empty for anonymous sessions).
func (*Session) RegenerateID ¶
RegenerateID assigns a fresh session ID. Call it right after a privilege change (for example a successful login), then Save, to avoid session fixation.