fix: harden login validation with proper JSON error responses
Change LoginHandler validation error from http.Error (plain text, mismatched Content-Type) to mw.RespondError (proper JSON {error:...} response). Add TestLogin_EmptyFields and TestLogin_MissingEmail tests verifying JSON error format.
This commit is contained in:
@@ -21,6 +21,7 @@ package auth
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
@@ -713,6 +714,56 @@ func TestLogin_InvalidRequest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestLogin_EmptyFields verifies that sending login with empty email/password
|
||||||
|
// returns HTTP 400 with a JSON error body containing an error field.
|
||||||
|
func TestLogin_EmptyFields(t *testing.T) {
|
||||||
|
_, _ = resetTestData(t)
|
||||||
|
|
||||||
|
handler := http.HandlerFunc(LoginHandler)
|
||||||
|
|
||||||
|
body := map[string]string{
|
||||||
|
"email": "",
|
||||||
|
"password": "",
|
||||||
|
}
|
||||||
|
w := testutils.MakeRequestNoAuth(handler, "POST", "/api/login", body, context.Background())
|
||||||
|
|
||||||
|
if w.Code != http.StatusBadRequest {
|
||||||
|
t.Errorf("expected status 400, got %d", w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp map[string]string
|
||||||
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||||
|
t.Fatalf("expected JSON error body, got: %s (parse error: %v)", w.Body.String(), err)
|
||||||
|
}
|
||||||
|
if _, ok := resp["error"]; !ok {
|
||||||
|
t.Errorf("expected JSON response with 'error' field, got: %v", resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestLogin_MissingEmail verifies that login without email field returns 400.
|
||||||
|
func TestLogin_MissingEmail(t *testing.T) {
|
||||||
|
_, _ = resetTestData(t)
|
||||||
|
|
||||||
|
handler := http.HandlerFunc(LoginHandler)
|
||||||
|
|
||||||
|
body := map[string]string{
|
||||||
|
"password": "secret123",
|
||||||
|
}
|
||||||
|
w := testutils.MakeRequestNoAuth(handler, "POST", "/api/login", body, context.Background())
|
||||||
|
|
||||||
|
if w.Code != http.StatusBadRequest {
|
||||||
|
t.Errorf("expected status 400, got %d", w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp map[string]string
|
||||||
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||||
|
t.Fatalf("expected JSON error body, got: %s (parse error: %v)", w.Body.String(), err)
|
||||||
|
}
|
||||||
|
if _, ok := resp["error"]; !ok {
|
||||||
|
t.Errorf("expected JSON response with 'error' field, got: %v", resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestRegister_NameTooLong tests that registration fails when the first name
|
// TestRegister_NameTooLong tests that registration fails when the first name
|
||||||
// exceeds 50 characters (the maximum allowed length).
|
// exceeds 50 characters (the maximum allowed length).
|
||||||
func TestRegister_NameTooLong(t *testing.T) {
|
func TestRegister_NameTooLong(t *testing.T) {
|
||||||
|
|||||||
@@ -313,13 +313,13 @@ func ValidateUKPhoneNumber(phone string) (string, error) {
|
|||||||
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var req LoginRequest
|
var req LoginRequest
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
http.Error(w, "invalid request", http.StatusBadRequest)
|
mw.RespondError(w, http.StatusBadRequest, "invalid request")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validators.Validate.Struct(&req); err != nil {
|
if err := validators.Validate.Struct(&req); err != nil {
|
||||||
log.Printf("Failed to process request: %v", err)
|
log.Printf("Login validation failed: %v", err)
|
||||||
http.Error(w, "Invalid request", http.StatusBadRequest)
|
mw.RespondError(w, http.StatusBadRequest, "Email and password are required")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user