package auth import ( "html/template" "log" "net/http" "github.com/alexedwards/scs/v2" ) // Handlers provides HTTP handlers for authentication type Handlers struct { service *Service sessions *scs.SessionManager middleware *Middleware templates *template.Template } // NewHandlers creates new auth handlers func NewHandlers(service *Service, sessions *scs.SessionManager, templates *template.Template) *Handlers { return &Handlers{ service: service, sessions: sessions, middleware: NewMiddleware(sessions), templates: templates, } } // Middleware returns the auth middleware for use in routes func (h *Handlers) Middleware() *Middleware { return h.middleware } // HandleLoginPage renders the login form func (h *Handlers) HandleLoginPage(w http.ResponseWriter, r *http.Request) { // If already logged in, redirect to dashboard if h.middleware.IsAuthenticated(r) { http.Redirect(w, r, "/", http.StatusSeeOther) return } data := struct { Error string CSRFToken string }{ Error: "", CSRFToken: h.middleware.GetCSRFToken(r), } if err := h.templates.ExecuteTemplate(w, "login.html", data); err != nil { http.Error(w, "Failed to render template", http.StatusInternalServerError) log.Printf("Error rendering login template: %v", err) } } // HandleLogin processes login form submission func (h *Handlers) HandleLogin(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { http.Error(w, "Failed to parse form", http.StatusBadRequest) return } username := r.FormValue("username") password := r.FormValue("password") if username == "" || password == "" { h.renderLoginError(w, r, "Username and password are required") return } user, err := h.service.Authenticate(username, password) if err != nil { log.Printf("Login failed for user %s: %v", username, err) h.renderLoginError(w, r, "Invalid username or password") return } // Regenerate session token to prevent session fixation if err := h.sessions.RenewToken(r.Context()); err != nil { http.Error(w, "Failed to create session", http.StatusInternalServerError) log.Printf("Failed to renew session token: %v", err) return } // Set user ID in session h.middleware.SetUserID(r, user.ID) log.Printf("User %s logged in successfully", username) http.Redirect(w, r, "/", http.StatusSeeOther) } // HandleLogout processes logout func (h *Handlers) HandleLogout(w http.ResponseWriter, r *http.Request) { if err := h.middleware.ClearSession(r); err != nil { log.Printf("Error clearing session: %v", err) } http.Redirect(w, r, "/login", http.StatusSeeOther) } func (h *Handlers) renderLoginError(w http.ResponseWriter, r *http.Request, errorMsg string) { data := struct { Error string CSRFToken string }{ Error: errorMsg, CSRFToken: h.middleware.GetCSRFToken(r), } w.WriteHeader(http.StatusUnauthorized) if err := h.templates.ExecuteTemplate(w, "login.html", data); err != nil { http.Error(w, "Failed to render template", http.StatusInternalServerError) log.Printf("Error rendering login template: %v", err) } }