From 97516f00e4206823f8c4485449f0080823ac5e35 Mon Sep 17 00:00:00 2001 From: xengineering Date: Sun, 21 Jun 2026 13:30:06 +0200 Subject: Add authenticate HTTP handler --- handlers.go | 39 +++++++++++++++++++++++++++++++++++++++ handlers_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/handlers.go b/handlers.go index e6d60fb..35e4998 100644 --- a/handlers.go +++ b/handlers.go @@ -18,6 +18,7 @@ var frontendEmbed embed.FS func registerHandlers(r *mux.Router, db *sql.DB) { r.Handle("/api/version", version(db)) r.Handle("/api/registration", register(db)) + r.Handle("/api/authentication", authenticate(db)) // frontend must come last to make sure /api takes precedence frontend, err := fs.Sub(frontendEmbed, "build/frontend/public") @@ -70,3 +71,41 @@ func register(db *sql.DB) http.Handler { } }) } + +func authenticate(db *sql.DB) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var data struct { + Username string + Password string + } + + dec := json.NewDecoder(r.Body) + dec.DisallowUnknownFields() + + err := dec.Decode(&data) + if err != nil { + log.Printf("Failed to decode: %v", err) + w.WriteHeader(http.StatusBadRequest) + return + } + + if data.Username == "" { + log.Print("Got registration request with empty username.") + w.WriteHeader(http.StatusBadRequest) + return + } + + if data.Password == "" { + log.Print("Got registration request with empty password.") + w.WriteHeader(http.StatusBadRequest) + return + } + + err = Authenticate(db, data.Username, data.Password) + if err != nil { + log.Print("Failed to authenticate user.") + w.WriteHeader(http.StatusUnauthorized) + return + } + }) +} diff --git a/handlers_test.go b/handlers_test.go index 3e226d0..c539b60 100644 --- a/handlers_test.go +++ b/handlers_test.go @@ -84,3 +84,33 @@ func TestHTTPRegister(t *testing.T) { testHandler(t, register, cases, setup) } + +func TestHTTPAuthenticate(t *testing.T) { + cases := []httpCase{ + {"basic", "POST", []byte(`{"username":"testuser","password":"mypass"}`), []byte(""), http.StatusOK}, + {"missing-password", "POST", []byte(`{"username":"testuser"}`), []byte(""), http.StatusBadRequest}, + {"missing-username", "POST", []byte(`{"password":"mypass"}`), []byte(""), http.StatusBadRequest}, + {"unknown-field", "POST", []byte(`{"username":"testuser","password":"mypass","foo":"bar"}`), []byte(""), http.StatusBadRequest}, + {"invalid-json", "POST", []byte(`{username":"testuser","password":"mypass"}`), []byte(""), http.StatusBadRequest}, + {"wrong-username", "POST", []byte(`{"username":"wronguser","password":"mypass"}`), []byte(""), http.StatusUnauthorized}, + {"wrong-password", "POST", []byte(`{"username":"testuser","password":"wrongpass"}`), []byte(""), http.StatusUnauthorized}, + } + + setup := func(t *testing.T) *sql.DB { + db, _ := emptyDB(t) + + err := MigrateToLatest(db) + if err != nil { + t.Fatalf("Could not migrate test database: %v", err) + } + + err = Register(db, "testuser", "mypass") + if err != nil { + t.Fatalf("Failed to register test user: %v", err) + } + + return db + } + + testHandler(t, authenticate, cases, setup) +} -- cgit v1.3.1