Auth: implement consent logic
diff --git a/core/auth/ui/hydra.go b/core/auth/ui/hydra.go
new file mode 100644
index 0000000..8a0dea0
--- /dev/null
+++ b/core/auth/ui/hydra.go
@@ -0,0 +1,162 @@
+package main
+
+import (
+	"bytes"
+	"crypto/tls"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io"
+	"net/http"
+	"net/url"
+	"strings"
+)
+
+type HydraClient struct {
+	httpClient *http.Client
+	host       string
+}
+
+func NewHydraClient(host string) *HydraClient {
+	return &HydraClient{
+		// TODO(giolekva): trust selfsigned-root-ca automatically on pods
+		&http.Client{
+			Transport: &http.Transport{
+				TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+			},
+		},
+		host,
+	}
+}
+
+type loginResp struct {
+	RedirectTo       string `json:"redirect_to"`
+	Error            string `json:"error"`
+	ErrorDebug       string `json:"error_debug"`
+	ErrorDescription string `json:"error_description"`
+	StatusCode       int    `json:"status_code"`
+}
+
+func (c *HydraClient) LoginAcceptChallenge(challenge, subject string) (string, error) {
+	req := &http.Request{
+		Method: http.MethodPut,
+		URL: &url.URL{
+			Scheme:   "https",
+			Host:     c.host,
+			Path:     "/oauth2/auth/requests/login/accept",
+			RawQuery: fmt.Sprintf("login_challenge=%s", challenge),
+		},
+		Header: map[string][]string{
+			"Content-Type": []string{"application/json"},
+		},
+		// TODO(giolekva): user stable userid instead
+		Body: io.NopCloser(strings.NewReader(fmt.Sprintf(`
+{
+    "subject": "%s",
+    "remember": true,
+    "remember_for": 3600
+}`, subject))),
+	}
+	resp, err := c.httpClient.Do(req)
+	if err != nil {
+		return "", err
+	}
+	var r loginResp
+	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
+		return "", err
+	}
+	if r.Error != "" {
+		return "", errors.New(r.Error)
+	}
+	return r.RedirectTo, nil
+}
+
+func (c *HydraClient) LoginRejectChallenge(challenge, message string) (string, error) {
+	req := &http.Request{
+		Method: http.MethodPut,
+		URL: &url.URL{
+			Scheme:   "https",
+			Host:     c.host,
+			Path:     "/oauth2/auth/requests/login/reject",
+			RawQuery: fmt.Sprintf("login_challenge=%s", challenge),
+		},
+		Header: map[string][]string{
+			"Content-Type": []string{"application/json"},
+		},
+		Body: io.NopCloser(strings.NewReader(fmt.Sprintf(`
+{
+    "error": "login_required %s"
+}`, message))),
+	}
+	resp, err := c.httpClient.Do(req)
+	if err != nil {
+		return "", err
+	}
+	var r loginResp
+	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
+		return "", err
+	}
+	if r.Error != "" {
+		return "", errors.New(r.Error)
+	}
+	return r.RedirectTo, nil
+}
+
+type RequestedConsent struct {
+	Challenge       string   `json:"challenge"`
+	Subject         string   `json:"subject"`
+	RequestedScopes []string `json:"requested_scope"`
+}
+
+func (c *HydraClient) GetConsentChallenge(challenge string) (RequestedConsent, error) {
+	var consent RequestedConsent
+	resp, err := c.httpClient.Get(fmt.Sprintf("https://%s/oauth2/auth/requests/consent?consent_challenge=%s", c.host, challenge))
+	if err != nil {
+		return consent, err
+	}
+	err = json.NewDecoder(resp.Body).Decode(&consent)
+	return consent, err
+}
+
+type consentAcceptReq struct {
+	GrantScope []string `json:"grant_scope"`
+	Session    session  `json:"session"`
+}
+
+type session struct {
+	IDToken map[string]string `json:"id_token"`
+}
+
+type consentAcceptResp struct {
+	RedirectTo string `json:"redirect_to"`
+}
+
+func (c *HydraClient) ConsentAccept(challenge string, scopes []string, idToken map[string]string) (string, error) {
+	accept := consentAcceptReq{scopes, session{idToken}}
+	var data bytes.Buffer
+	if err := json.NewEncoder(&data).Encode(accept); err != nil {
+		return "", err
+	}
+	req := &http.Request{
+		Method: http.MethodPut,
+		URL: &url.URL{
+			Scheme:   "https",
+			Host:     c.host,
+			Path:     "/oauth2/auth/requests/consent/accept",
+			RawQuery: fmt.Sprintf("challenge=%s", challenge),
+		},
+		Header: map[string][]string{
+			"Content-Type": []string{"application/json"},
+		},
+		Body: io.NopCloser(&data),
+	}
+	resp, err := c.httpClient.Do(req)
+	if err != nil {
+		return "", err
+	}
+	var r consentAcceptResp
+	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
+		return "", err
+	}
+	return r.RedirectTo, err
+}