session

package
v1.3.2 Latest Latest
Warning

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

Go to latest
Published: Apr 6, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package session provides server-side session management middleware for celeris.

Sessions are identified by a cookie (default), header, or query parameter, and data is stored server-side in a pluggable Store. The default [MemoryStore] uses sharded maps with a background cleanup goroutine, suitable for single-instance deployments.

Basic usage with defaults (in-memory store, 24h sessions):

server.Use(session.New())

Custom cookie name and timeouts:

server.Use(session.New(session.Config{
    CookieName:      "myapp_sid",
    IdleTimeout:     15 * time.Minute,
    AbsoluteTimeout: 2 * time.Hour,
}))

Accessing the Session

Use FromContext to retrieve the session from downstream handlers:

s := session.FromContext(c)
s.Set("user", "admin")
name, ok := s.Get("user")

Modified sessions are automatically saved after the handler chain returns. Call Session.Destroy to invalidate a session, or Session.Regenerate to issue a new session ID.

Session Fixation Prevention

Applications MUST call Session.Regenerate (or Session.Reset) after any authentication state change to prevent session fixation attacks:

func loginHandler(c *celeris.Context) error {
    // ... validate credentials ...
    s := session.FromContext(c)
    if err := s.Regenerate(); err != nil {
        return err
    }
    s.Set("user", authenticatedUser)
    return nil
}

Pluggable Extractors

CookieExtractor, HeaderExtractor, QueryExtractor, and ChainExtractor control where the session ID is read from. When a non-cookie extractor is used, the session ID is returned in a response header named after Config.CookieName so API clients can capture it.

Out-of-Band Access

NewHandler returns a Handler providing both the middleware and out-of-band session access via Handler.GetByID for admin tools or background jobs.

Timeout Semantics

IdleTimeout (default 30m) controls server-side expiry per session. AbsoluteTimeout (default 24h) caps total session lifetime. Set AbsoluteTimeout to -1 to disable it. Session.SetIdleTimeout overrides idle timeout for individual sessions ("remember me" flows).

Custom Stores

Implement the Store interface to back sessions with any storage backend. All Store methods receive a context.Context propagated from the request for cancellation and deadline support.

Security

CookieSecure defaults to false for development convenience. Production deployments MUST set CookieSecure: true to prevent cookie transmission over unencrypted connections.

Index

Examples

Constants

View Source
const ContextKey = "session"

ContextKey is the context store key for the session object.

Variables

View Source
var ErrSessionDestroyed = errors.New("session: cannot save a destroyed session")

ErrSessionDestroyed is returned when Save is called after Destroy.

Functions

func BoolPtr

func BoolPtr(v bool) *bool

BoolPtr returns a pointer to the given bool value. Use this to explicitly set Config.CookieHTTPOnly:

session.New(session.Config{CookieHTTPOnly: session.BoolPtr(false)})

func IntPtr

func IntPtr(v int) *int

IntPtr returns a pointer to the given int value. Use this to explicitly set Config.CookieMaxAge:

session.New(session.Config{CookieMaxAge: session.IntPtr(0)}) // session cookie

func New

func New(config ...Config) celeris.HandlerFunc

New creates a session middleware with the given config.

Example
package main

import (
	"github.com/goceleris/celeris/middleware/session"
)

func main() {
	// Zero-config: in-memory store, 24h sessions, Lax cookies.
	_ = session.New()
}
Example (ContextAwareStore)
package main

import (
	"github.com/goceleris/celeris"

	"github.com/goceleris/celeris/middleware/session"
)

func main() {
	// The middleware passes the request context to every Store method,
	// so network-backed stores can respect cancellation and deadlines.
	_ = session.New(session.Config{
		Store: session.NewMemoryStore(), // swap with a Redis/SQL store
	})
	_ = celeris.HandlerFunc(func(c *celeris.Context) error {
		s := session.FromContext(c)
		// Store.Save receives c.Context() automatically on post-handler save.
		s.Set("user", "admin")
		return nil
	})
}
Example (CustomCookie)
package main

import (
	"time"

	"github.com/goceleris/celeris/middleware/session"
)

func main() {
	// Custom cookie name and timeouts.
	_ = session.New(session.Config{
		CookieName:      "myapp_sid",
		CookieSecure:    true,
		IdleTimeout:     15 * time.Minute,
		AbsoluteTimeout: 2 * time.Hour,
	})
}
Example (CustomStore)
package main

import (
	"github.com/goceleris/celeris/middleware/session"
)

func main() {
	// Use a custom store (e.g., Redis).
	var myStore session.Store // = redis.NewStore(...)
	_ = session.New(session.Config{
		Store: myStore,
	})
}
Example (DisableAbsoluteTimeout)
package main

import (
	"time"

	"github.com/goceleris/celeris/middleware/session"
)

func main() {
	// Disable absolute timeout enforcement (sessions live until idle timeout).
	_ = session.New(session.Config{
		AbsoluteTimeout: -1,
		IdleTimeout:     30 * time.Minute,
	})
}
Example (HeaderExtractor)
package main

import (
	"github.com/goceleris/celeris/middleware/session"
)

func main() {
	// API clients: read session ID from a request header.
	_ = session.New(session.Config{
		Extractor: session.HeaderExtractor("X-Session-ID"),
	})
}
Example (QueryExtractor)
package main

import (
	"github.com/goceleris/celeris/middleware/session"
)

func main() {
	// Read session ID from a query parameter.
	_ = session.New(session.Config{
		Extractor: session.QueryExtractor("sid"),
	})
}
Example (SessionOnlyCookie)
package main

import (
	"github.com/goceleris/celeris/middleware/session"
)

func main() {
	// Browser-session-scoped cookie (no Max-Age, deleted when browser closes).
	_ = session.New(session.Config{
		CookieMaxAge: session.IntPtr(0),
	})
}

Types

type Config

type Config struct {
	// Skip defines a function to skip this middleware for certain requests.
	Skip func(c *celeris.Context) bool

	// SkipPaths lists paths to skip (exact match).
	SkipPaths []string

	// Store is the session backend. Default: NewMemoryStore().
	Store Store

	// Extractor extracts the session ID from the request. Default:
	// CookieExtractor(CookieName). When a non-cookie extractor is used,
	// the session ID is also set in a response header named after the
	// cookie name for API clients.
	Extractor Extractor

	// CookieName is the session cookie name. Default: "celeris_session".
	CookieName string

	// CookiePath is the cookie Path attribute. Default: "/".
	CookiePath string

	// CookieDomain is the cookie Domain attribute.
	CookieDomain string

	// CookieMaxAge is the cookie Max-Age in seconds.
	// nil means "use default (86400 = 24h)". A non-nil pointer selects
	// the exact value: 0 for a session cookie (no Max-Age attribute),
	// positive for an explicit lifetime.
	//
	// Note: CookieMaxAge controls browser-side cookie lifetime and is
	// independent of IdleTimeout (server-side store expiry). If CookieMaxAge
	// is shorter than IdleTimeout, the browser will discard the cookie while
	// the server still holds the session data. If longer, the browser may
	// send a cookie for a session the server has already evicted. For
	// consistent behavior, align CookieMaxAge with IdleTimeout (e.g.,
	// CookieMaxAge = intPtr(int(IdleTimeout.Seconds()))).
	CookieMaxAge *int

	// CookieSecure flags the cookie for HTTPS-only transmission.
	CookieSecure bool

	// CookieHTTPOnly prevents client-side scripts from accessing the cookie.
	// nil means "use default (true)". Set to a non-nil pointer to
	// explicitly enable or disable: BoolPtr(false) disables HTTPOnly
	// for JS-based session management.
	CookieHTTPOnly *bool

	// CookieSameSite controls cross-site request cookie behavior.
	// Default: celeris.SameSiteLaxMode.
	CookieSameSite celeris.SameSite

	// IdleTimeout is how long a session can be idle before expiring.
	// Default: 30 minutes.
	IdleTimeout time.Duration

	// AbsoluteTimeout is the maximum lifetime of a session regardless of
	// activity. Default: 24 hours. Set to 0 for the default, or -1 to
	// disable absolute timeout enforcement entirely.
	AbsoluteTimeout time.Duration

	// KeyGenerator generates new session IDs. Default: 32-byte crypto/rand hex.
	KeyGenerator func() string

	// ErrorHandler is called when a store operation fails. If nil, the error
	// is returned up the middleware chain.
	ErrorHandler func(c *celeris.Context, err error) error
}

Config defines the session middleware configuration.

type Extractor

type Extractor func(c *celeris.Context) string

Extractor is a function that extracts the session ID from a request. The default extractor reads the session cookie. For API clients, use HeaderExtractor or QueryExtractor instead.

func ChainExtractor

func ChainExtractor(extractors ...Extractor) Extractor

ChainExtractor returns an Extractor that tries each extractor in order and returns the first non-empty result. This is useful when session IDs may arrive via different transport mechanisms (e.g., cookie for browsers, header for API clients).

func CookieExtractor

func CookieExtractor(cookieName string) Extractor

CookieExtractor returns an Extractor that reads the session ID from the named cookie.

func HeaderExtractor

func HeaderExtractor(headerName string) Extractor

HeaderExtractor returns an Extractor that reads the session ID from the named request header. Use this for API clients that cannot store cookies.

func QueryExtractor

func QueryExtractor(paramName string) Extractor

QueryExtractor returns an Extractor that reads the session ID from the named query parameter.

type Handler

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

Handler holds the session middleware and provides methods for out-of-band session access (e.g., admin tools, background jobs).

func NewHandler

func NewHandler(config ...Config) *Handler

NewHandler creates a session Handler that exposes both the middleware and out-of-band session access via Handler.GetByID.

func (*Handler) GetByID

func (h *Handler) GetByID(ctx context.Context, id string) (*Session, error)

GetByID retrieves a session by its raw ID without an HTTP context. This is intended for admin tools, background jobs, or WebSocket handlers that need to inspect session data outside of the middleware pipeline.

The returned Session is read-only: use Get, GetString, GetInt, GetBool, GetFloat64, Keys, Len, ID, and IsFresh. Calling Save, Regenerate, or Destroy on a GetByID session panics with a descriptive message because no store or context is bound. Returns nil and no error if the session does not exist.

func (*Handler) Middleware

func (h *Handler) Middleware() celeris.HandlerFunc

Middleware returns the celeris.HandlerFunc to pass to server.Use().

type MemoryStoreConfig

type MemoryStoreConfig struct {
	// Shards is the number of lock shards. Default: runtime.NumCPU().
	Shards int

	// CleanupInterval is how often expired entries are evicted.
	// Default: 1 minute.
	CleanupInterval time.Duration

	// CleanupContext, if set, controls cleanup goroutine lifetime.
	// When the context is cancelled, the cleanup goroutine stops.
	// If nil, cleanup runs until the process exits.
	CleanupContext context.Context
}

MemoryStoreConfig configures the in-memory session store.

type Session

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

Session holds per-request session data backed by a Store.

Session is designed for single-goroutine-per-request access and is NOT safe for concurrent use from multiple goroutines within the same request. If you need to access the session from multiple goroutines (e.g., a background task spawned mid-request), you must synchronize access externally.

func FromContext

func FromContext(c *celeris.Context) *Session

FromContext retrieves the Session from the request context. Returns nil if the session middleware was not applied or the request was skipped.

Example
package main

import (
	"github.com/goceleris/celeris"

	"github.com/goceleris/celeris/middleware/session"
)

func main() {
	handler := func(c *celeris.Context) error {
		s := session.FromContext(c)
		s.Set("user", "admin")
		name, _ := s.Get("user")
		_ = name // "admin"
		return nil
	}
	_ = handler
}

func (*Session) Clear

func (s *Session) Clear()

Clear removes all user data from the session and marks it as modified. The internal _abs_exp timestamp is preserved.

func (*Session) Delete

func (s *Session) Delete(key string)

Delete removes a key from the session. The session is marked as modified only if the key was actually present.

func (*Session) Destroy

func (s *Session) Destroy() error

Destroy invalidates the session by clearing data and deleting it from the store.

func (*Session) Get

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

Get returns the value for key from the session data.

func (*Session) GetBool

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

GetBool returns the value for key as a bool. Returns false if the key is missing or the value is not a bool.

func (*Session) GetFloat64

func (s *Session) GetFloat64(key string) float64

GetFloat64 returns the value for key as a float64. Returns 0 if the key is missing or the value is not a float64.

func (*Session) GetInt

func (s *Session) GetInt(key string) int

GetInt returns the value for key as an int. Returns 0 if the key is missing or the value is not an int.

func (*Session) GetString

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

GetString returns the value for key as a string. Returns "" if the key is missing or the value is not a string.

func (*Session) ID

func (s *Session) ID() string

ID returns the session identifier.

func (*Session) IsFresh

func (s *Session) IsFresh() bool

IsFresh returns true if this is a newly created session (no prior cookie).

func (*Session) Keys

func (s *Session) Keys() []string

Keys returns a sorted list of all user-visible keys in the session data. The internal "_abs_exp" key is excluded.

func (*Session) Len

func (s *Session) Len() int

Len returns the number of user-visible key-value pairs in the session. The internal "_abs_exp" key is excluded from the count.

func (*Session) Regenerate

func (s *Session) Regenerate() error

Regenerate issues a new session ID while preserving data. The old session is deleted from the store. The data is saved under the new ID by the post-handler save (since modified is set to true).

The internal _abs_exp timestamp is reset to the current time so the regenerated session gets a fresh absolute timeout window.

Applications MUST call Regenerate after authentication state changes (e.g., login, privilege escalation) to prevent session fixation attacks.

func (*Session) Reset

func (s *Session) Reset() error

Reset is a convenience method that combines Session.Clear, Session.Regenerate, and marks the session as modified in a single call. Useful for "log out and start fresh" flows.

Note: Reset is not atomic. If Clear succeeds but Regenerate fails (e.g., store error on Delete), the session data is already cleared but the old session ID is retained. Callers should handle the returned error accordingly.

Example
package main

import (
	"github.com/goceleris/celeris"

	"github.com/goceleris/celeris/middleware/session"
)

func main() {
	handler := func(c *celeris.Context) error {
		s := session.FromContext(c)
		// Logout: clear all data and regenerate session ID in one call.
		return s.Reset()
	}
	_ = handler
}

func (*Session) Save

func (s *Session) Save() error

Save persists the session data to the store. This is called automatically after the handler chain when the session has been modified; call it explicitly only if you need to guarantee persistence mid-handler. Returns ErrSessionDestroyed if the session has been destroyed.

func (*Session) Set

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

Set stores a key-value pair in the session and marks it as modified. The reserved key "_abs_exp" is silently rejected to protect internal absolute-timeout bookkeeping.

func (*Session) SetIdleTimeout

func (s *Session) SetIdleTimeout(d time.Duration)

SetIdleTimeout overrides the idle timeout for this individual session. The post-handler save uses this value instead of the config-level Config.IdleTimeout. Pass 0 to revert to the config default.

Example
package main

import (
	"time"

	"github.com/goceleris/celeris"

	"github.com/goceleris/celeris/middleware/session"
)

func main() {
	handler := func(c *celeris.Context) error {
		s := session.FromContext(c)
		// "Remember me" — extend this specific session's idle timeout.
		s.SetIdleTimeout(7 * 24 * time.Hour)
		s.Set("user", "admin")
		return nil
	}
	_ = handler
}

type Store

type Store interface {
	// Get retrieves session data by ID. Returns nil map and no error if the
	// session does not exist or has expired.
	Get(ctx context.Context, id string) (map[string]any, error)

	// Save persists session data with the given expiry duration.
	Save(ctx context.Context, id string, data map[string]any, expiry time.Duration) error

	// Delete removes a session by ID.
	Delete(ctx context.Context, id string) error

	// Reset wipes all sessions from the store.
	Reset(ctx context.Context) error
}

Store defines the interface for session storage backends.

All methods accept a context.Context to support cancellation and deadline propagation. The middleware passes the request context obtained from celeris.Context.Context.

func NewMemoryStore

func NewMemoryStore(config ...MemoryStoreConfig) Store

NewMemoryStore creates an in-memory session store backed by sharded maps. A background goroutine periodically evicts expired sessions.

MemoryStore should be created once and reused for the lifetime of the application. Each call to NewMemoryStore spawns a cleanup goroutine; call [MemoryStore.Close] to stop it if you need deterministic shutdown (e.g., in tests). If you provide MemoryStoreConfig.CleanupContext, cancelling that context also stops the goroutine.

Jump to

Keyboard shortcuts

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