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

session

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Oct 17, 2026 License: MIT Imports: 11 Imported by: 0

README

Go Reference License Stay with Ukraine

session

session provides secure, signed cookie sessions for browser apps. The MVP is cookie-only: the whole session lives in an HMAC-SHA256 signed cookie, so there is no server-side store to run. Zero dependencies, standard library only.

It complements token-based auth rather than replacing it: session owns cookie and browser state; an auth package owns subjects, passwords and tokens.

Install

go get github.com/goloop/session

Quick start

m := session.New(secret,
	session.WithName("sid"),
	session.WithSecure(true),                 // HTTPS only in production
	session.WithSameSite(http.SameSiteLaxMode),
	session.WithTTL(24*time.Hour),
)

// Read (or start), mutate, persist.
s := m.LoadOrNew(r)
s.Set("theme", "dark")
err := m.Save(w, s)

// Log out.
m.Destroy(w)

With the middleware:

http.Handle("/", m.Middleware(handler))

// inside handler:
s, _ := session.From(r.Context())
s.Set("seen", "1")
_ = m.Save(w, s)

Security

  • Cookie is HttpOnly, SameSite=Lax and Secure by default; pass WithSecure(false) only for local HTTP development.
  • New panics on a signing key shorter than 32 bytes; call Session.RegenerateID after login (then Save) to avoid session fixation.
  • Payload is signed with HMAC-SHA256 and versioned, so the format can evolve without breaking old cookies.
  • Key rotation is built in: the primary key signs, any key added with WithKey verifies, so you can roll keys without invalidating live sessions.

Scope

The MVP does not yet include a server-side store, encrypted payloads or CSRF (planned). It does not do user management, password hashing or OAuth.

Documentation

License

MIT - see LICENSE.

Release v1.0.0

Verify cookie signatures without copying their signed prefix. The v1 release keeps the existing import path and public API. Future incompatible changes require a new major version.

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.

The payload is SIGNED, NOT ENCRYPTED. Subject and every value are stored as base64(JSON): the client cannot forge them, but can read them. Do not put secrets in a session. Encrypted payloads are planned (WithEncryption).

Logout is stateless: Destroy clears the cookie in this response, but a copy the client already captured stays valid until its ExpiresAt, because there is no server-side store to revoke it. Keep the TTL short, consider WithAbsoluteTTL for a hard lifetime cap, and for immediate revocation pair this with a server-side denylist of session IDs.

A loaded session is not proof of authentication. Middleware and LoadOrNew return a fresh empty session when the cookie is missing or invalid, so a successful From does not mean the request is authenticated: check Subject (or your own flag) before granting access, or a protected route can fail open.

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

View Source
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")

	// ErrNilSession is returned by Save when given a nil session.
	ErrNilSession = errors.New("session: nil session")
)

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

func New(secret []byte, opts ...Option) *Manager

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

func (m *Manager) Load(r *http.Request) (*Session, error)

Load reads and verifies the session cookie from the request.

func (*Manager) LoadOrNew

func (m *Manager) LoadOrNew(r *http.Request) *Session

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

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

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.

func (*Manager) Save

func (m *Manager) Save(w http.ResponseWriter, s *Session) error

Save writes the session as a signed cookie. It assigns an ID and timestamps when missing and refreshes the expiry to now+TTL.

type Option

type Option func(*Manager)

Option configures a Manager.

func WithAbsoluteTTL added in v0.2.0

func WithAbsoluteTTL(d time.Duration) Option

WithAbsoluteTTL caps the total lifetime of a session from its creation, independent of activity. The default TTL slides forward on every Save, so an active session can live indefinitely; with an absolute TTL a session older than CreatedAt+d is rejected on Load even if it was refreshed recently. A non-positive duration disables the cap (the default).

func WithClock

func WithClock(now func() time.Time) Option

WithClock overrides the time source (for testing). A nil function is ignored.

func WithDomain

func WithDomain(domain string) Option

WithDomain sets the cookie Domain attribute.

func WithKey

func WithKey(key []byte) Option

WithKey adds another key for verification, enabling key rotation: the primary key (passed to New) signs, any configured key verifies.

func WithName

func WithName(name string) Option

WithName sets the cookie name (default "session"). It panics on a name that is not a valid cookie token (RFC 6265): an invalid name makes net/http emit an empty Set-Cookie header, silently dropping the session, so it must fail at startup like a weak key.

func WithPath

func WithPath(path string) Option

WithPath sets the cookie Path attribute (default "/").

func WithSameSite

func WithSameSite(mode http.SameSite) Option

WithSameSite sets the SameSite attribute (default Lax).

func WithSecure

func WithSecure(secure bool) Option

WithSecure sets the Secure attribute (send only over HTTPS). It defaults to true; pass false only for local HTTP development.

func WithTTL

func WithTTL(d time.Duration) Option

WithTTL sets the session lifetime (default 24h).

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 From

func From(ctx context.Context) (*Session, bool)

From returns the session stored in the context by Middleware.

func (*Session) Delete

func (s *Session) Delete(key string)

Delete removes a key.

func (*Session) Get

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

Get returns the value for key, or "" if absent.

func (*Session) RegenerateID

func (s *Session) RegenerateID() error

RegenerateID assigns a fresh session ID. Call it right after a privilege change (for example a successful login), then Save, to avoid session fixation.

func (*Session) Set

func (s *Session) Set(key, value string)

Set stores a value under key.

Jump to

Keyboard shortcuts

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