Skip to content

Sessions

λ Cosmos provides server-side session management with automatic persistence, expiration handling, and session regeneration for security.

type Session struct {
ID string
Data map[string]any
ExpiresAt time.Time
}
type SessionDriver interface {
Get(ctx context.Context, id string) (Session, error)
Save(ctx context.Context, session Session, ttl time.Duration) error
Delete(ctx context.Context, id string) error
}

Set up sessions with the session middleware and a cache-backed driver:

import (
"github.com/studiolambda/cosmos/framework"
"github.com/studiolambda/cosmos/framework/cache"
"github.com/studiolambda/cosmos/framework/session"
"github.com/studiolambda/cosmos/framework/middleware"
"github.com/studiolambda/cosmos/contract/request"
)
app := framework.New()
// Use in-memory cache as the session store
store := cache.NewMemory(2*time.Hour, 10*time.Minute)
driver := session.NewCacheDriver(store)
app.Use(middleware.Recover())
app.Use(session.Middleware(driver))
app.Get("/profile", func(w http.ResponseWriter, r *http.Request) error {
s := request.MustSession(r)
name, _ := s.Get("name")
return response.JSON(w, http.StatusOK, map[string]any{
"name": name,
})
})
app.Post("/login", func(w http.ResponseWriter, r *http.Request) error {
s := request.MustSession(r)
s.Put("name", "Alice")
s.Put("authenticated", true)
// Regenerate the session ID after authentication to prevent fixation attacks
s.Regenerate()
return response.Status(w, http.StatusOK)
})

The middleware handles the full session lifecycle:

  1. Load — Reads the session ID from the cookie and loads the session from the driver. If no cookie exists or the session is not found, a new session is created.
  2. Inject — Places the session into the request context.
  3. Persist — Before the response header is written, checks if the session needs saving:
    • If the session has expired, it regenerates the ID and extends the TTL.
    • If the session is about to expire (within the expiration delta), it extends the TTL.
    • If the session was regenerated, it deletes the old session from the store.
    • If any data changed, it saves the session and sets the cookie.
session.Middleware(driver)

Uses these defaults:

  • Cookie name: cosmos.session
  • Path: /
  • Secure: true
  • HttpOnly: true (always)
  • SameSite: Lax
  • TTL: 2 hours
  • Expiration delta: 15 minutes (extends when this close to expiring)
session.MiddlewareWith(driver, session.MiddlewareOptions{
Name: "my_session",
Path: "/",
Domain: "example.com",
Secure: true,
SameSite: http.SameSiteStrictMode,
Partitioned: true,
TTL: 24 * time.Hour,
ExpirationDelta: 1 * time.Hour,
Key: contract.SessionKey,
})
s := request.MustSession(r)
value, exists := s.Get("user_id")
if !exists {
// key not in session
}
s.Put("user_id", "123")
s.Put("role", "admin")
s.Delete("temporary_flag")
s.Clear()

Removes all session data but keeps the session ID and expiration intact.

err := s.Regenerate()

Generates a new session ID (UUID v7) while preserving all session data. The middleware automatically deletes the old session from the store. Use this after authentication events to prevent session fixation attacks.

s.Extend(time.Now().Add(24 * time.Hour))
s.HasExpired() // true if past expiration time
s.ExpiresSoon(15*time.Minute) // true if expiring within 15 minutes
s.ExpiresAt() // returns the expiration time.Time
s.HasChanged() // true if any Put/Delete/Clear/Extend/Regenerate was called
s.HasRegenerated() // true if Regenerate() was called
s.MarkAsUnchanged() // reset the change flag (prevents persistence)

The middleware only persists the session and sets the cookie when HasChanged() is true. Call MarkAsUnchanged() if you want to suppress persistence for a particular request.

The built-in session.CacheDriver stores sessions in any contract.Cache implementation:

// Using in-memory cache
driver := session.NewCacheDriver(cache.NewMemory(2*time.Hour, 10*time.Minute))
// Using Redis
driver := session.NewCacheDriver(cache.NewRedis(&cache.RedisOptions{
Addr: "localhost:6379",
}))

Sessions are stored with a key prefix (default: cosmos.sessions). Customize it with:

driver := session.NewCacheDriverWith(store, session.CacheDriverOptions{
prefix: "myapp.sessions",
})

Implement the contract.SessionDriver interface to store sessions in any backend (database, file system, etc.).