| 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 |
| } |