package api import ( "encoding/json" "net/http" "github.com/mjensen/weimar/internal/auth" ) type loginRequest struct { Username string `json:"username"` Password string `json:"password"` } type registerRequest struct { Username string `json:"username"` Password string `json:"password"` } func (a *API) HandleLogin(w http.ResponseWriter, r *http.Request) { var req loginRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid JSON body") return } if req.Username == "" || req.Password == "" { writeError(w, http.StatusBadRequest, "username and password are required") return } user, token, err := a.Auth.Login(r.Context(), req.Username, req.Password) if err != nil { if err == auth.ErrInvalidCredentials { writeError(w, http.StatusUnauthorized, "invalid username or password") return } writeError(w, http.StatusInternalServerError, "internal error") return } http.SetCookie(w, &http.Cookie{ Name: auth.SessionCookieName, Value: token, Path: "/", HttpOnly: true, Secure: a.Cfg.Server.SecureCookie(), SameSite: http.SameSiteLaxMode, }) writeJSON(w, http.StatusOK, user) } func (a *API) HandleRegister(w http.ResponseWriter, r *http.Request) { if !a.Cfg.Auth.AllowRegistration { writeError(w, http.StatusForbidden, "registration is closed") return } var req registerRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid JSON body") return } user, err := auth.Register(r.Context(), a.DB, req.Username, req.Password, false) if err != nil { switch err { case auth.ErrUsernameTooShort, auth.ErrPasswordTooShort, auth.ErrUsernameTaken: writeError(w, http.StatusBadRequest, err.Error()) return default: writeError(w, http.StatusInternalServerError, "internal error") return } } // Auto-login after successful registration so the user doesn't need to // make a separate login call. If this fails we still return the user. if _, token, err := a.Auth.Login(r.Context(), req.Username, req.Password); err == nil { http.SetCookie(w, &http.Cookie{ Name: auth.SessionCookieName, Value: token, Path: "/", HttpOnly: true, Secure: a.Cfg.Server.SecureCookie(), SameSite: http.SameSiteLaxMode, }) } writeJSON(w, http.StatusCreated, user) } func (a *API) HandleLogout(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie(auth.SessionCookieName) if err != nil { writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) return } a.Auth.Logout(r.Context(), cookie.Value) http.SetCookie(w, &http.Cookie{ Name: auth.SessionCookieName, Value: "", Path: "/", HttpOnly: true, SameSite: http.SameSiteLaxMode, MaxAge: -1, }) writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) }