blob: 8775d9ef1f035824afaea91c2f16fde18d728227 [file] [log] [blame]
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: "http",
Host: c.host,
Path: "/admin/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: "http",
Host: c.host,
Path: "/admin/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("http://%s/admin/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: "http",
Host: c.host,
Path: "/admin/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
}