all repos — weimar @ 0ba068faac3861b09c7dc896d508fa45e594e23e

Unnamed repository; edit this file 'description' to name the repository.

internal/api/auth.go (view raw)

  1package api
  2
  3import (
  4	"encoding/json"
  5	"net/http"
  6
  7	"codeberg.org/maxwelljensen/weimar/internal/auth"
  8)
  9
 10type loginRequest struct {
 11	Username string `json:"username"`
 12	Password string `json:"password"`
 13}
 14
 15type registerRequest struct {
 16	Username string `json:"username"`
 17	Password string `json:"password"`
 18}
 19
 20func (a *API) HandleLogin(w http.ResponseWriter, r *http.Request) {
 21	var req loginRequest
 22	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
 23		writeError(w, http.StatusBadRequest, "invalid JSON body")
 24		return
 25	}
 26	if req.Username == "" || req.Password == "" {
 27		writeError(w, http.StatusBadRequest, "username and password are required")
 28		return
 29	}
 30
 31	user, token, err := a.Auth.Login(r.Context(), req.Username, req.Password)
 32	if err != nil {
 33		if err == auth.ErrInvalidCredentials {
 34			writeError(w, http.StatusUnauthorized, "invalid username or password")
 35			return
 36		}
 37		writeError(w, http.StatusInternalServerError, "internal error")
 38		return
 39	}
 40
 41	http.SetCookie(w, &http.Cookie{
 42		Name:     auth.SessionCookieName,
 43		Value:    token,
 44		Path:     "/",
 45		HttpOnly: true,
 46		Secure:   a.Cfg.Server.SecureCookie(),
 47		SameSite: http.SameSiteLaxMode,
 48	})
 49
 50	writeJSON(w, http.StatusOK, user)
 51}
 52
 53func (a *API) HandleRegister(w http.ResponseWriter, r *http.Request) {
 54	if !a.Cfg.Auth.AllowRegistration {
 55		writeError(w, http.StatusForbidden, "registration is closed")
 56		return
 57	}
 58
 59	var req registerRequest
 60	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
 61		writeError(w, http.StatusBadRequest, "invalid JSON body")
 62		return
 63	}
 64
 65	user, err := auth.Register(r.Context(), a.DB, req.Username, req.Password, false)
 66	if err != nil {
 67		switch err {
 68		case auth.ErrUsernameTooShort, auth.ErrPasswordTooShort, auth.ErrUsernameTaken:
 69			writeError(w, http.StatusBadRequest, err.Error())
 70			return
 71		default:
 72			writeError(w, http.StatusInternalServerError, "internal error")
 73			return
 74		}
 75	}
 76
 77	// Auto-login after successful registration so the user doesn't need to
 78	// make a separate login call. If this fails we still return the user.
 79	if _, token, err := a.Auth.Login(r.Context(), req.Username, req.Password); err == nil {
 80		http.SetCookie(w, &http.Cookie{
 81			Name:     auth.SessionCookieName,
 82			Value:    token,
 83			Path:     "/",
 84			HttpOnly: true,
 85			Secure:   a.Cfg.Server.SecureCookie(),
 86			SameSite: http.SameSiteLaxMode,
 87		})
 88	}
 89
 90	writeJSON(w, http.StatusCreated, user)
 91}
 92
 93func (a *API) HandleLogout(w http.ResponseWriter, r *http.Request) {
 94	cookie, err := r.Cookie(auth.SessionCookieName)
 95	if err != nil {
 96		writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
 97		return
 98	}
 99
100	a.Auth.Logout(r.Context(), cookie.Value)
101
102	http.SetCookie(w, &http.Cookie{
103		Name:     auth.SessionCookieName,
104		Value:    "",
105		Path:     "/",
106		HttpOnly: true,
107		SameSite: http.SameSiteLaxMode,
108		MaxAge:   -1,
109	})
110
111	writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
112}