first commit

This commit is contained in:
2026-07-16 10:13:46 +03:30
commit 423c528b2f
42 changed files with 7298 additions and 0 deletions
+10
View File
@@ -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"
+74
View File
@@ -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
}