upaas/internal/handlers/auth.go
clawbot b1dc8fcc4e Add CSRF protection to state-changing POST endpoints
Add gorilla/csrf middleware to protect all HTML-serving routes against
cross-site request forgery attacks. The webhook endpoint is excluded
since it uses secret-based authentication.

Changes:
- Add gorilla/csrf v1.7.3 dependency
- Add CSRF() middleware method using session secret as key
- Apply CSRF middleware to all HTML route groups in routes.go
- Pass CSRF token to all templates via addGlobals helper
- Add {{ .CSRFField }} / {{ $.CSRFField }} hidden inputs to all forms

Closes #11
2026-02-15 14:17:55 -08:00

83 lines
2.1 KiB
Go

package handlers
import (
"net/http"
"git.eeqj.de/sneak/upaas/templates"
)
// HandleLoginGET returns the login page handler.
func (h *Handlers) HandleLoginGET() http.HandlerFunc {
tmpl := templates.GetParsed()
return func(writer http.ResponseWriter, request *http.Request) {
data := h.addGlobals(map[string]any{}, request)
err := tmpl.ExecuteTemplate(writer, "login.html", data)
if err != nil {
h.log.Error("template execution failed", "error", err)
http.Error(writer, "Internal Server Error", http.StatusInternalServerError)
}
}
}
// HandleLoginPOST handles the login form submission.
func (h *Handlers) HandleLoginPOST() http.HandlerFunc {
tmpl := templates.GetParsed()
return func(writer http.ResponseWriter, request *http.Request) {
parseErr := request.ParseForm()
if parseErr != nil {
http.Error(writer, "Bad Request", http.StatusBadRequest)
return
}
username := request.FormValue("username")
password := request.FormValue("password")
data := h.addGlobals(map[string]any{
"Username": username,
}, request)
if username == "" || password == "" {
data["Error"] = "Username and password are required"
_ = tmpl.ExecuteTemplate(writer, "login.html", data)
return
}
user, authErr := h.auth.Authenticate(request.Context(), username, password)
if authErr != nil {
data["Error"] = "Invalid username or password"
_ = tmpl.ExecuteTemplate(writer, "login.html", data)
return
}
sessionErr := h.auth.CreateSession(writer, request, user)
if sessionErr != nil {
h.log.Error("failed to create session", "error", sessionErr)
data["Error"] = "Failed to create session"
_ = tmpl.ExecuteTemplate(writer, "login.html", data)
return
}
http.Redirect(writer, request, "/", http.StatusSeeOther)
}
}
// HandleLogout handles logout requests.
func (h *Handlers) HandleLogout() http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
destroyErr := h.auth.DestroySession(writer, request)
if destroyErr != nil {
h.log.Error("failed to destroy session", "error", destroyErr)
}
http.Redirect(writer, request, "/login", http.StatusSeeOther)
}
}