auth-proxy: proxies only authenticated requests to upstream, redirects to login page otherwise (#103)

* auth-proxy: inspects authenticated user

* ingress: chart and use in rpuppy

* auth-proxy: make it optional in rpuppy

* kratos: whitelist env pub/priv domains for auth return_to addr

* url-shortener: put behind auth-proxy

* pihole: replace oauth2-client with auth-proxy

* auth-proxy: fix upstream uri generation

* pihole: remove old chart using oauth2

* auth-proxy: remove temporary values file

* url-shortener: check x-user header for authentication

* auth: fix allowed_return_urls list

* auth-proxy: fix current address generation logic

---------

Co-authored-by: Giorgi Lekveishvili <lekva@gl-mbp-m1-max.local>
diff --git a/core/auth/ui/Makefile b/core/auth/ui/Makefile
index f3b9b63..23ae76b 100644
--- a/core/auth/ui/Makefile
+++ b/core/auth/ui/Makefile
@@ -1,5 +1,8 @@
 repo_name ?= dtabidze
 podman ?= docker
+ifeq ($(podman), podman)
+manifest_dest=docker://docker.io/$(repo_name)/pcloud-installer:latest
+endif
 
 clean:
 	rm -f server server_*
@@ -32,5 +35,5 @@
 
 push: push_arm64 push_amd64
 	$(podman) manifest create $(repo_name)/auth-ui:latest $(repo_name)/auth-ui:arm64 $(repo_name)/auth-ui:amd64
-	$(podman) manifest push $(repo_name)/auth-ui:latest
+	$(podman) manifest push $(repo_name)/auth-ui:latest $(manifest_dest)
 	$(podman) manifest rm $(repo_name)/auth-ui:latest
diff --git a/core/auth/ui/main.go b/core/auth/ui/main.go
index 7cb1f4d..3de264c 100644
--- a/core/auth/ui/main.go
+++ b/core/auth/ui/main.go
@@ -239,9 +239,14 @@
 		// 	HttpOnly: true,
 		// })
 	}
+	returnTo := r.Form.Get("return_to")
 	flow, ok := r.Form["flow"]
 	if !ok {
-		http.Redirect(w, r, s.kratos+"/self-service/login/browser", http.StatusSeeOther)
+		addr := s.kratos + "/self-service/login/browser"
+		if returnTo != "" {
+			addr += fmt.Sprintf("?return_to=%s", returnTo)
+		}
+		http.Redirect(w, r, addr, http.StatusSeeOther)
 		return
 	}
 	csrfToken, err := getCSRFToken("login", flow[0], r.Cookies())
@@ -289,6 +294,32 @@
 	return resp, nil
 }
 
+func postFormToKratos(flowType, flow string, cookies []*http.Cookie, data url.Values) (*http.Response, error) {
+	jar, err := cookiejar.New(nil)
+	if err != nil {
+		return nil, err
+	}
+	client := &http.Client{
+		Jar: jar,
+		Transport: &http.Transport{
+			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+		},
+		CheckRedirect: func(req *http.Request, via []*http.Request) error {
+			return http.ErrUseLastResponse
+		},
+	}
+	b, err := url.Parse(*kratos + "/self-service/" + flowType + "/browser")
+	if err != nil {
+		return nil, err
+	}
+	client.Jar.SetCookies(b, cookies)
+	resp, err := client.PostForm(fmt.Sprintf(*kratos+"/self-service/"+flowType+"?flow=%s", flow), data)
+	if err != nil {
+		return nil, err
+	}
+	return resp, nil
+}
+
 type logoutResp struct {
 	LogoutURL string `json:"logout_url"`
 }
@@ -360,6 +391,7 @@
 	if err != nil {
 		return err
 	}
+	fmt.Printf("++ %s\n", respBody)
 	t, err := regogo.Get(string(respBody), "input.ui.messages[0].type")
 	if err != nil {
 		return err
@@ -384,21 +416,17 @@
 		http.Redirect(w, r, s.kratos+"/self-service/login/browser", http.StatusSeeOther)
 		return
 	}
-	req := loginReq{
-		CSRFToken: r.FormValue("csrf_token"),
-		Method:    "password",
-		Password:  r.FormValue("password"),
-		Username:  r.FormValue("username"),
+	req := url.Values{
+		"csrf_token": []string{r.FormValue("csrf_token")},
+		"method":     []string{"password"},
+		"password":   []string{r.FormValue("password")},
+		"identifier": []string{r.FormValue("username")},
 	}
-	var reqBody bytes.Buffer
-	if err := json.NewEncoder(&reqBody).Encode(req); err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-		return
-	}
-	resp, err := postToKratos("login", flow[0], r.Cookies(), &reqBody)
-	if err == nil {
-		err = extractError(resp.Body)
-	}
+	resp, err := postFormToKratos("login", flow[0], r.Cookies(), req)
+	fmt.Printf("--- %d\n", resp.StatusCode)
+	var vv bytes.Buffer
+	io.Copy(&vv, resp.Body)
+	fmt.Println(vv.String())
 	if err != nil {
 		if challenge, _ := r.Cookie("login_challenge"); challenge != nil {
 			redirectTo, err := s.hydra.LoginRejectChallenge(challenge.Value, err.Error())
@@ -429,7 +457,11 @@
 		http.Redirect(w, r, redirectTo, http.StatusSeeOther)
 		return
 	}
-	http.Redirect(w, r, "/", http.StatusSeeOther)
+	if resp.StatusCode == http.StatusSeeOther {
+		http.Redirect(w, r, resp.Header.Get("Location"), http.StatusSeeOther)
+	} else {
+		http.Redirect(w, r, "/", http.StatusSeeOther)
+	}
 }
 
 func (s *Server) logout(w http.ResponseWriter, r *http.Request) {