From 0d987f5c97cc8c0e205193ef8c67745ac981d5bf Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Thu, 3 Apr 2025 12:36:34 +0200 Subject: Fix login and register --- api/handlers/handlers.go | 44 +++++++++++---- cmd/api/main.go | 12 +++-- frontend/nuxt.config.ts | 1 + frontend/pages/index.vue | 21 ++------ frontend/pages/login.vue | 15 ++++-- frontend/pages/register.vue | 128 ++++++++++++++++++++++++++++++++++++++++++++ go.mod | 3 +- go.sum | 6 ++- pkg/utils.go | 24 ++++++++- 9 files changed, 213 insertions(+), 41 deletions(-) create mode 100644 frontend/pages/register.vue diff --git a/api/handlers/handlers.go b/api/handlers/handlers.go index 7d5fd10..cc7a9d9 100644 --- a/api/handlers/handlers.go +++ b/api/handlers/handlers.go @@ -2,44 +2,66 @@ package handlers import ( "encoding/json" + "log/slog" "net/http" "github.com/boozec/rahanna/api/auth" "github.com/boozec/rahanna/api/database" + utils "github.com/boozec/rahanna/pkg" "golang.org/x/crypto/bcrypt" ) func RegisterUser(w http.ResponseWriter, r *http.Request) { + slog.Info("POST /register") var user database.User err := json.NewDecoder(r.Body).Decode(&user) if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) + utils.JsonError(&w, err.Error()) + return + } + + if len(user.Password) < 4 { + utils.JsonError(&w, "password too short") + return + } + + var storedUser database.User + db, _ := database.GetDb() + result := db.Where("username = ?", user.Username).First(&storedUser) + + if result.Error == nil { + utils.JsonError(&w, "user with this username already exists") return } hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + utils.JsonError(&w, err.Error()) return } user.Password = string(hashedPassword) - db, _ := database.GetDb() - - result := db.Create(&user) + result = db.Create(&user) if result.Error != nil { - http.Error(w, result.Error.Error(), http.StatusInternalServerError) + utils.JsonError(&w, result.Error.Error()) + return + } + + token, err := auth.GenerateJWT(user.ID) + if err != nil { + utils.JsonError(&w, err.Error()) return } - w.WriteHeader(http.StatusCreated) + json.NewEncoder(w).Encode(map[string]string{"token": token}) } func LoginUser(w http.ResponseWriter, r *http.Request) { + slog.Info("POST /login") var inputUser database.User err := json.NewDecoder(r.Body).Decode(&inputUser) if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) + utils.JsonError(&w, err.Error()) return } @@ -48,19 +70,19 @@ func LoginUser(w http.ResponseWriter, r *http.Request) { db, _ := database.GetDb() result := db.Where("username = ?", inputUser.Username).First(&storedUser) if result.Error != nil { - http.Error(w, "Invalid credentials", http.StatusUnauthorized) + utils.JsonError(&w, "invalid credentials") return } err = bcrypt.CompareHashAndPassword([]byte(storedUser.Password), []byte(inputUser.Password)) if err != nil { - http.Error(w, "Invalid credentials", http.StatusUnauthorized) + utils.JsonError(&w, "invalid credentials") return } token, err := auth.GenerateJWT(storedUser.ID) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + utils.JsonError(&w, err.Error()) return } diff --git a/cmd/api/main.go b/cmd/api/main.go index 2120d41..ff80c17 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -1,22 +1,26 @@ package main import ( + "log/slog" + "net/http" "os" "github.com/boozec/rahanna/api/database" "github.com/boozec/rahanna/api/handlers" "github.com/gorilla/mux" - "net/http" + "github.com/rs/cors" ) func main() { database.InitDb(os.Getenv("DATABASE_URL")) r := mux.NewRouter() - r.HandleFunc("/register", handlers.RegisterUser).Methods(http.MethodPost) - r.HandleFunc("/login", handlers.LoginUser).Methods(http.MethodPost) + r.HandleFunc("/auth/register", handlers.RegisterUser).Methods(http.MethodPost) + r.HandleFunc("/auth/login", handlers.LoginUser).Methods(http.MethodPost) - if err := http.ListenAndServe(":8080", r); err != nil { + slog.Info("Serving on :8080") + handler := cors.AllowAll().Handler(r) + if err := http.ListenAndServe(":8080", handler); err != nil { panic(err) } } diff --git a/frontend/nuxt.config.ts b/frontend/nuxt.config.ts index 3e1df8c..df48718 100644 --- a/frontend/nuxt.config.ts +++ b/frontend/nuxt.config.ts @@ -2,6 +2,7 @@ export default defineNuxtConfig({ compatibilityDate: "2024-11-01", devtools: { enabled: false }, + ssr: false, modules: ["@nuxt/ui"], css: ["~/assets/css/main.css"], runtimeConfig: { diff --git a/frontend/pages/index.vue b/frontend/pages/index.vue index d097617..110db25 100644 --- a/frontend/pages/index.vue +++ b/frontend/pages/index.vue @@ -1,20 +1,7 @@ - + diff --git a/frontend/pages/login.vue b/frontend/pages/login.vue index 0be0372..5fdb689 100644 --- a/frontend/pages/login.vue +++ b/frontend/pages/login.vue @@ -83,7 +83,7 @@ const handleSubmit = async (event) => { try { error.value = null; isLoading.value = true; - fetch(`${config.public.apiBase}/auth/login`, { + await fetch(`${config.public.apiBase}/auth/login`, { method: "POST", headers: { "Content-Type": "application/json", @@ -92,23 +92,28 @@ const handleSubmit = async (event) => { username: username.value, password: password.value, }), - }).then((response) => { + }).then(async (response) => { + const body = await response.json(); if (response.status != 200) { toast.add({ title: "Login Failed", - description: response.body, + description: body.error, color: "error", }); } else { toast.add({ title: "Login Successful", - description: "You have been successfully logged in.", + description: "You have been successfully logged in", color: "success", }); + + localStorage.setItem("token", body.token); + setTimeout(() => { + window.location.href = "/play"; + }, 1000); } }); } catch (err) { - console.error("Login failed:", err); error.value = err.response?.data?.message || "An error occurred during login"; diff --git a/frontend/pages/register.vue b/frontend/pages/register.vue new file mode 100644 index 0000000..824b53b --- /dev/null +++ b/frontend/pages/register.vue @@ -0,0 +1,128 @@ + + + diff --git a/go.mod b/go.mod index 882b0b3..b0c1d27 100644 --- a/go.mod +++ b/go.mod @@ -24,9 +24,10 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.6.1 // indirect + github.com/rs/cors v1.11.1 // indirect go.uber.org/multierr v1.10.0 // indirect golang.org/x/net v0.34.0 // indirect - golang.org/x/sync v0.10.0 // indirect + golang.org/x/sync v0.12.0 // indirect golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect diff --git a/go.sum b/go.sum index 5ef9159..9b02a5b 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= +github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -65,8 +67,8 @@ golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= diff --git a/pkg/utils.go b/pkg/utils.go index f5a443e..9246854 100644 --- a/pkg/utils.go +++ b/pkg/utils.go @@ -1,6 +1,11 @@ package utils -import "golang.org/x/crypto/bcrypt" +import ( + "encoding/json" + "net/http" + + "golang.org/x/crypto/bcrypt" +) func HashPassword(password string) (string, error) { bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14) @@ -11,3 +16,20 @@ func CheckPasswordHash(password, hash string) bool { err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) return err == nil } + +// Set a JSON response with status code 400 +func JsonError(w *http.ResponseWriter, error string) { + payloadMap := map[string]string{"error": error} + + (*w).Header().Set("Content-Type", "application/json") + (*w).WriteHeader(http.StatusBadRequest) + + payload, err := json.Marshal(payloadMap) + + if err != nil { + (*w).WriteHeader(http.StatusBadGateway) + (*w).Write([]byte(err.Error())) + } else { + (*w).Write(payload) + } +} -- cgit v1.2.3-18-g5258