all repos — weimar @ master

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

internal/server/middleware.go (view raw)

 1package server
 2
 3import (
 4	"context"
 5	"log/slog"
 6	"net/http"
 7	"strings"
 8
 9	"codeberg.org/maxwelljensen/weimar/internal/api"
10	"codeberg.org/maxwelljensen/weimar/internal/auth"
11)
12
13// requireAuth wraps a handler with session cookie authentication.
14// Unauthenticated requests receive a 401 JSON error.
15func (s *Server) requireAuth(next http.HandlerFunc) http.HandlerFunc {
16	return func(w http.ResponseWriter, r *http.Request) {
17		cookie, err := r.Cookie(auth.SessionCookieName)
18		if err != nil {
19			writeJSONError(w, http.StatusUnauthorized, "unauthorized")
20			return
21		}
22
23		user, err := s.auth.Authenticate(r.Context(), cookie.Value)
24		if err != nil {
25			slog.Debug("auth failed", "reason", err)
26			writeJSONError(w, http.StatusUnauthorized, "unauthorized")
27			return
28		}
29
30		ctx := context.WithValue(r.Context(), api.CtxKeyUser, user)
31		next(w, r.WithContext(ctx))
32	}
33}
34
35// requireAuthOrPublic wraps a handler with optional auth.
36// If the user has a valid session, it sets the user in context.
37// If not, the request passes through without a user context.
38// This is controlled by the require_auth_for_browse config setting.
39func (s *Server) requireAuthOrPublic(next http.HandlerFunc) http.HandlerFunc {
40	return func(w http.ResponseWriter, r *http.Request) {
41		if s.cfg.Auth.RequireAuthForBrowse {
42			// Act like requireAuth — reject unauthenticated.
43			s.requireAuth(next).ServeHTTP(w, r)
44			return
45		}
46
47		// Try to authenticate, but don't reject if it fails.
48		cookie, err := r.Cookie(auth.SessionCookieName)
49		if err != nil {
50			next(w, r)
51			return
52		}
53
54		user, err := s.auth.Authenticate(r.Context(), cookie.Value)
55		if err != nil {
56			next(w, r)
57			return
58		}
59
60		ctx := context.WithValue(r.Context(), api.CtxKeyUser, user)
61		next(w, r.WithContext(ctx))
62	}
63}
64
65// writeJSONError writes a JSON error response.
66func writeJSONError(w http.ResponseWriter, status int, msg string) {
67	w.Header().Set("Content-Type", "application/json")
68	w.WriteHeader(status)
69	w.Write([]byte(`{"error":"` + strings.ReplaceAll(msg, `"`, `\"`) + `"}`))
70}