From 56b77ad18c90e47299c46d6f0fde8abe3ff6d16d Mon Sep 17 00:00:00 2001 From: roerick Date: Sun, 19 Oct 2025 15:50:47 -0600 Subject: switch to mux prefix for router --- handlers/login.go | 29 +++++++++++++++++++++-------- main.go | 35 ++++++++++++++++++----------------- middleware/auth.go | 7 ++++++- session/session.go | 2 +- 4 files changed, 46 insertions(+), 27 deletions(-) diff --git a/handlers/login.go b/handlers/login.go index 831cd36..6da835a 100644 --- a/handlers/login.go +++ b/handlers/login.go @@ -2,6 +2,7 @@ package handlers import ( "net/http" + "net/url" "text/template" "context" @@ -13,6 +14,7 @@ import ( var loginTmpl = template.Must(template.New("login").Parse(` Login
+

@@ -20,15 +22,15 @@ var loginTmpl = template.Must(template.New("login").Parse(` `)) func LoginGET(w http.ResponseWriter, r *http.Request) { - _ = loginTmpl.Execute(w, nil) + _ = loginTmpl.Execute(w, struct{ Next string }{ + Next: r.URL.Query().Get("next"), + }) } - func LoginPOST(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } - // Parse credentials if err := r.ParseForm(); err != nil { http.Error(w, "bad form", http.StatusBadRequest) return @@ -36,24 +38,35 @@ func LoginPOST(w http.ResponseWriter, r *http.Request) { user := r.Form.Get("username") pass := r.Form.Get("password") - // One-time BSD Auth (OpenBSD's BSD Authentication, not PAM) ok, _ := bsdauth.Authenticate(user, pass, "auth-myapp") - // `auth_userokay()` returns nonzero on success. :contentReference[oaicite:2]{index=2} if !ok { http.Error(w, "invalid credentials", http.StatusUnauthorized) return } - // Rotate session token to prevent fixation, then set user if err := session.Manager.RenewToken(r.Context()); err != nil { http.Error(w, "server error", http.StatusInternalServerError) return } session.Manager.Put(r.Context(), "user", user) - http.Redirect(w, r, "/", http.StatusSeeOther) -} + // Prefer ?next=... when present, but guard against open redirects. + next := r.Form.Get("next") + if next == "" { + next = "/" + } else { + u, err := url.Parse(next) + if err != nil || u.IsAbs() || len(u.Host) > 0 { + next = "/" + } + // Optional: only allow same-site paths + if u.Path == "" || u.Path[0] != '/' { + next = "/" + } + } + http.Redirect(w, r, next, http.StatusSeeOther) // PRG pattern +} func LogoutPOST(w http.ResponseWriter, r *http.Request) { _ = session.Manager.Destroy(r.Context()) http.Redirect(w, r, "/login", http.StatusSeeOther) diff --git a/main.go b/main.go index f381fa8..e39e8c5 100644 --- a/main.go +++ b/main.go @@ -29,38 +29,39 @@ func init() { func main() { session.Init() - // 2) Router + attach SCS middleware correctly - r := mux.NewRouter() - r.Use(session.Manager.LoadAndSave) + mux := mux.NewRouter() + mux.Use(session.Manager.LoadAndSave) - // 3) Routes - r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("Hello, World!")) }) - // Blog endpoints - r.HandleFunc("/blogs", handlers.CreateBlog).Methods(http.MethodGet) - r.HandleFunc("/blogs", handlers.StoreBlog).Methods(http.MethodPost) - r.HandleFunc("/blogs/{id}", handlers.GetBlog).Methods(http.MethodGet) + mux.HandleFunc("/blogs", handlers.CreateBlog).Methods(http.MethodGet) + mux.HandleFunc("/blogs", handlers.StoreBlog).Methods(http.MethodPost) + mux.HandleFunc("/blogs/{id}", handlers.GetBlog).Methods(http.MethodGet) - r.HandleFunc("/login", handlers.LoginGET) - r.HandleFunc("/login/submit", handlers.LoginPOST) + mux.HandleFunc("GET /posts/{slug}", func(w http.ResponseWriter, r *http.Request) { + slug := r.PathValue("slug") + fmt.Fprintf(w, "Post: %s", slug) + }) + + mux.HandleFunc("/login", handlers.LoginGET) + mux.HandleFunc("/login/submit", handlers.LoginPOST) // Logout (POST is recommended; allow GET only if you must) - r.HandleFunc("/logout", handlers.LogoutPOST) + mux.HandleFunc("/logout", handlers.LogoutPOST) - // r.HandleFunc("/blogs/{id}", handlers.UpdateBlog).Methods(http.MethodPut) - // r.HandleFunc("/blogs/{id}", handlers.DeleteBlog).Methods(http.MethodDelete) + // mux.HandleFunc("/blogs/{id}", handlers.UpdateBlog).Methods(http.MethodPut) + // mux.HandleFunc("/blogs/{id}", handlers.DeleteBlog).Methods(http.MethodDelete) // Example protected route (SCS must already be on the chain) - r.Handle("/whoami", middleware.RequireAuth(http.HandlerFunc(handlers.Dashboard))) + mux.Handle("/whoami", middleware.RequireAuth(http.HandlerFunc(handlers.Dashboard))) - // 4) Start the server using the router you wrapped log.Println("Starting server on :8090") srv := &http.Server{ Addr: ":8090", - Handler: r, + Handler: mux, } log.Fatal(srv.ListenAndServe()) } diff --git a/middleware/auth.go b/middleware/auth.go index 947985a..a42d59c 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -2,16 +2,21 @@ package middleware import ( "net/http" + "net/url" "wyo.town/netzah/session" ) + func RequireAuth(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !session.Manager.Exists(r.Context(), "user") { - http.Redirect(w, r, "/login", http.StatusFound) + nextURL := url.QueryEscape(r.URL.RequestURI()) + http.Redirect(w, r, "/login?next="+nextURL, http.StatusFound) return } next.ServeHTTP(w, r) }) } + + diff --git a/session/session.go b/session/session.go index d80e4f9..37b6dd7 100644 --- a/session/session.go +++ b/session/session.go @@ -17,7 +17,7 @@ func Init() { sm.Cookie.Name = "sid" sm.Cookie.HttpOnly = true sm.Cookie.SameSite = http.SameSiteLaxMode - sm.Cookie.Secure = true // set false only on localhost HTTP during dev + sm.Cookie.Secure = false // set false only on localhost HTTP during dev Manager = sm } -- cgit v1.2.3