first commit
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
package session
|
||||
|
||||
// UserIDKey is the string key under which we store the logged-in user's
|
||||
// numeric ID inside the session (via sessions.Put(ctx, UserIDKey, id)).
|
||||
//
|
||||
// This constant exists so the key is spelled exactly once, in one place -
|
||||
// every other file that touches this key (login, logout, the auth
|
||||
// middleware) imports this constant instead of retyping the raw string
|
||||
// "user_id", which would risk a typo silently breaking authentication.
|
||||
const UserIDKey = "user_id"
|
||||
@@ -0,0 +1,74 @@
|
||||
// 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 <form>
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user