all repos — weimar @ 3ab9da929b53350b9e5a860642317e5a8f5b3750

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	"github.com/mjensen/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		SameSite: http.SameSiteLaxMode,
 47	})
 48
 49	writeJSON(w, http.StatusOK, user)
 50}
 51
 52func (a *API) HandleRegister(w http.ResponseWriter, r *http.Request) {
 53	if !a.Cfg.Auth.AllowRegistration {
 54		writeError(w, http.StatusForbidden, "registration is closed")
 55		return
 56	}
 57
 58	var req registerRequest
 59	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
 60		writeError(w, http.StatusBadRequest, "invalid JSON body")
 61		return
 62	}
 63
 64	user, err := auth.Register(r.Context(), a.DB, req.Username, req.Password, false)
 65	if err != nil {
 66		switch err {
 67		case auth.ErrUsernameTooShort, auth.ErrPasswordTooShort, auth.ErrUsernameTaken:
 68			writeError(w, http.StatusBadRequest, err.Error())
 69			return
 70		default:
 71			writeError(w, http.StatusInternalServerError, "internal error")
 72			return
 73		}
 74	}
 75
 76	// Auto-login after successful registration so the user doesn't need to
 77	// make a separate login call. If this fails we still return the user.
 78	if _, token, err := a.Auth.Login(r.Context(), req.Username, req.Password); err == nil {
 79		http.SetCookie(w, &http.Cookie{
 80			Name:     auth.SessionCookieName,
 81			Value:    token,
 82			Path:     "/",
 83			HttpOnly: true,
 84			SameSite: http.SameSiteLaxMode,
 85		})
 86	}
 87
 88	writeJSON(w, http.StatusCreated, user)
 89}
 90
 91func (a *API) HandleLogout(w http.ResponseWriter, r *http.Request) {
 92	cookie, err := r.Cookie(auth.SessionCookieName)
 93	if err != nil {
 94		writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
 95		return
 96	}
 97
 98	a.Auth.Logout(r.Context(), cookie.Value)
 99
100	http.SetCookie(w, &http.Cookie{
101		Name:     auth.SessionCookieName,
102		Value:    "",
103		Path:     "/",
104		HttpOnly: true,
105		SameSite: http.SameSiteLaxMode,
106		MaxAge:   -1,
107	})
108
109	writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
110}