// Package session builds and configures the application's server-side // session manager. We use github.com/alexedwards/scs (SCS) for the session // framework itself, backed by Redis for storage. // // "Server-side session" means: the browser only ever holds a random, // meaningless token in a cookie. All the actual session DATA (which user // is logged in, etc.) lives in Redis, keyed by that token. This is in // contrast to storing data directly inside a signed/encrypted cookie - // server-side sessions can be instantly revoked (just delete the Redis // key), don't grow the cookie size as you store more data, and never // expose their contents to the browser at all. package session import ( "net/http" "time" "github.com/alexedwards/scs/redisstore" "github.com/alexedwards/scs/v2" "github.com/gomodule/redigo/redis" "git.hamidsoltani.com/hamid/go-simple-api/internal/config" ) // New builds a *scs.SessionManager wired up to Redis, with cookie settings // appropriate for an authentication system. func New(cfg config.Config) *scs.SessionManager { // redigo's connection pool - conceptually identical to *sql.DB's pool // from the database package, just for Redis instead of MySQL. Dial is // a function the pool calls whenever it needs to open a new // connection. pool := &redis.Pool{ MaxIdle: 10, Dial: func() (redis.Conn, error) { return redis.Dial("tcp", cfg.RedisAddr) }, } manager := scs.New() // By default scs stores session data IN MEMORY, which would be lost on // every restart and wouldn't work at all if you ever ran more than one // instance of this app behind a load balancer. Setting .Store swaps // the storage backend to Redis while keeping the exact same // Put/Get/Destroy API - the rest of the app never needs to know Redis // is involved at all. manager.Store = redisstore.New(pool) // How long a session stays valid after creation. manager.Lifetime = 24 * time.Hour // --- Cookie security settings --- manager.Cookie.Name = "session_id" // HttpOnly: JavaScript in the browser cannot read this cookie via // document.cookie. Blocks a large class of XSS-based session theft. manager.Cookie.HttpOnly = true // SameSite=Lax: the cookie IS sent on normal top-level navigation // (e.g. clicking a link to this site, or Google redirecting back to // our OAuth callback), but is NOT sent on cross-site requests // triggered by another page (e.g. a malicious auto-submitting
// pointed at our /logout). This is our primary CSRF defense for the // cookie-based session. manager.Cookie.SameSite = http.SameSiteLaxMode // Secure: when true, the browser will refuse to send this cookie over // plain HTTP, only HTTPS. We only turn this on in production, because // enabling it during local development (over http://localhost) would // silently break the cookie entirely. manager.Cookie.Secure = cfg.Env == "production" return manager }