summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--handlers/login.go29
-rw-r--r--main.go35
-rw-r--r--middleware/auth.go7
-rw-r--r--session/session.go2
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(`
<!doctype html><title>Login</title>
<form method="post" action="/login/submit">
+ <input type="hidden" name="next" value="{{.Next}}">
<label>User <input name="username" autocomplete="username"></label><br>
<label>Pass <input name="password" type="password" autocomplete="current-password"></label><br>
<button>Login</button>
@@ -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
}