From 08bbcf18b1207153983261652b4a43a9b36f386c Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Tue, 20 Jan 2026 11:34:33 -1000 Subject: Add session-based authentication Implement secure authentication using scs session manager with SQLite backing store and bcrypt password hashing. - Add users and sessions tables (migration 004) - Create internal/auth package with Service, Middleware, and Handlers - Protect all routes except /login, /logout, /static/* - Add login page template and logout button to dashboard - Default credentials: admin/changeme (configurable via env vars) Co-Authored-By: Claude Opus 4.5 --- internal/auth/middleware.go | 50 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 internal/auth/middleware.go (limited to 'internal/auth/middleware.go') diff --git a/internal/auth/middleware.go b/internal/auth/middleware.go new file mode 100644 index 0000000..7710328 --- /dev/null +++ b/internal/auth/middleware.go @@ -0,0 +1,50 @@ +package auth + +import ( + "net/http" + + "github.com/alexedwards/scs/v2" +) + +const SessionKeyUserID = "user_id" + +// Middleware provides authentication middleware +type Middleware struct { + sessions *scs.SessionManager +} + +// NewMiddleware creates a new auth middleware +func NewMiddleware(sessions *scs.SessionManager) *Middleware { + return &Middleware{sessions: sessions} +} + +// RequireAuth redirects to login if not authenticated +func (m *Middleware) RequireAuth(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !m.IsAuthenticated(r) { + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + next.ServeHTTP(w, r) + }) +} + +// IsAuthenticated checks if the current request has a valid session +func (m *Middleware) IsAuthenticated(r *http.Request) bool { + return m.sessions.Exists(r.Context(), SessionKeyUserID) +} + +// GetUserID returns the authenticated user's ID from the session +func (m *Middleware) GetUserID(r *http.Request) int64 { + return m.sessions.GetInt64(r.Context(), SessionKeyUserID) +} + +// SetUserID sets the user ID in the session (called after successful login) +func (m *Middleware) SetUserID(r *http.Request, userID int64) { + m.sessions.Put(r.Context(), SessionKeyUserID, userID) +} + +// ClearSession removes the user ID from the session (called on logout) +func (m *Middleware) ClearSession(r *http.Request) error { + return m.sessions.Destroy(r.Context()) +} -- cgit v1.2.3