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}