package main import ( "context" "crypto/rand" "encoding/hex" "encoding/json" "io" "log" "net/http" ) type ApiHandler struct { db *Database authToken *string } const authTokenCookieName = "auth-token" const isAuthorizedContextKey = "is-authorized" func (h *ApiHandler) ServeLoginPost(writer http.ResponseWriter, request *http.Request) { bodyReader := request.Body body, err := io.ReadAll(bodyReader) _ = bodyReader.Close() if err != nil { http.Error(writer, err.Error(), http.StatusBadRequest) return } type LoginBody struct { Password string `json:"password"` } loginBody := LoginBody{} err = json.Unmarshal(body, &loginBody) if err != nil { http.Error(writer, err.Error(), http.StatusBadRequest) return } success, err := h.db.ValidateRootPassword(loginBody.Password) if err != nil { log.Println("Error logging in:", err) http.Error(writer, "failed to read database", http.StatusInternalServerError) return } if !success { http.Error(writer, "invalid password", http.StatusUnauthorized) return } rawAuthToken := make([]byte, 128) _, _ = rand.Read(rawAuthToken) authToken := hex.EncodeToString(rawAuthToken) h.authToken = &authToken cookie := http.Cookie{} cookie.Name = authTokenCookieName cookie.Value = authToken cookie.Secure = true cookie.HttpOnly = true http.SetCookie(writer, &cookie) writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) err = json.NewEncoder(writer).Encode(map[string]interface{}{}) if err != nil { http.Error(writer, "failed to serialize results", http.StatusInternalServerError) return } } func (h *ApiHandler) ServeLogoutPost(writer http.ResponseWriter, request *http.Request) { cookie, _ := request.Cookie(authTokenCookieName) if cookie != nil { cookie := http.Cookie{} cookie.Name = authTokenCookieName cookie.Value = "" cookie.Secure = true cookie.HttpOnly = true http.SetCookie(writer, &cookie) } h.authToken = nil writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) err := json.NewEncoder(writer).Encode(map[string]interface{}{}) if err != nil { http.Error(writer, "failed to serialize results", http.StatusInternalServerError) return } } func (h *ApiHandler) ProcessAuth(next http.Handler, required bool) http.Handler { return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { isAuthorized := false cookie, _ := request.Cookie(authTokenCookieName) if cookie != nil { isAuthorized = h.authToken != nil && *h.authToken == cookie.Value } if !isAuthorized && required { http.Error(writer, "authentication required", http.StatusUnauthorized) return } next.ServeHTTP(writer, request.WithContext(context.WithValue(request.Context(), isAuthorizedContextKey, isAuthorized))) }) } func IsAuthorized(request *http.Request) bool { value := request.Context().Value(isAuthorizedContextKey) return value != nil && value.(bool) }