git.iko.soy/typeable/oauth2-proxy
on master help sign in
Migrate all requests to new builder pattern
5314245Joel Speed
pkg/validation/options.go+29−28
@@ -83,34 +83,33 @@ func Validate(o *options.Options) error {
83
83
84 logger.Printf("Performing OIDC Discovery...")
84 logger.Printf("Performing OIDC Discovery...")
85
85
86− if req, err := http.NewRequest("GET", strings.TrimSuffix(o.OIDCIssuerURL, "/")+"/.well-known/openid-configuration", nil); err == nil {
86+ requestURL := strings.TrimSuffix(o.OIDCIssuerURL, "/") + "/.well-known/openid-configuration"
87− if body, err := requests.Request(req); err == nil {
87+ body, err := requests.New(requestURL).
88−
88+ WithContext(ctx).
89− // Prefer manually configured URLs. It's a bit unclear
89+ UnmarshalJSON()
90− // why you'd be doing discovery and also providing the URLs
90+ if err != nil {
91− // explicitly though...
92− if o.LoginURL == "" {
93− o.LoginURL = body.Get("authorization_endpoint").MustString()
94− }
95−
96− if o.RedeemURL == "" {
97− o.RedeemURL = body.Get("token_endpoint").MustString()
98− }
99−
100− if o.OIDCJwksURL == "" {
101− o.OIDCJwksURL = body.Get("jwks_uri").MustString()
102− }
103−
104− if o.ProfileURL == "" {
105− o.ProfileURL = body.Get("userinfo_endpoint").MustString()
106− }
107−
108− o.SkipOIDCDiscovery = true
109− } else {
110− logger.Printf("error: failed to discover OIDC configuration: %v", err)
91+ logger.Printf("error: failed to discover OIDC configuration: %v", err)
111− }
112 } else {
92 } else {
113− logger.Printf("error: failed parsing OIDC discovery URL: %v", err)
93+ // Prefer manually configured URLs. It's a bit unclear
94+ // why you'd be doing discovery and also providing the URLs
95+ // explicitly though...
96+ if o.LoginURL == "" {
97+ o.LoginURL = body.Get("authorization_endpoint").MustString()
98+ }
99+
100+ if o.RedeemURL == "" {
101+ o.RedeemURL = body.Get("token_endpoint").MustString()
102+ }
103+
104+ if o.OIDCJwksURL == "" {
105+ o.OIDCJwksURL = body.Get("jwks_uri").MustString()
106+ }
107+
108+ if o.ProfileURL == "" {
109+ o.ProfileURL = body.Get("userinfo_endpoint").MustString()
110+ }
111+
112+ o.SkipOIDCDiscovery = true
114 }
113 }
115 }
114 }
116
115
@@ -385,10 +384,12 @@ func newVerifierFromJwtIssuer(jwtIssuer jwtIssuer) (*oidc.IDTokenVerifier, error
385 if err != nil {
384 if err != nil {
386 // Try as JWKS URI
385 // Try as JWKS URI
387 jwksURI := strings.TrimSuffix(jwtIssuer.issuerURI, "/") + "/.well-known/jwks.json"
386 jwksURI := strings.TrimSuffix(jwtIssuer.issuerURI, "/") + "/.well-known/jwks.json"
388− _, err := http.NewRequest("GET", jwksURI, nil)
387+ resp, err := requests.New(jwksURI).Do()
389 if err != nil {
388 if err != nil {
390 return nil, err
389 return nil, err
391 }
390 }
391+ resp.Body.Close()
392+
392 verifier = oidc.NewVerifier(jwtIssuer.issuerURI, oidc.NewRemoteKeySet(context.Background(), jwksURI), config)
393 verifier = oidc.NewVerifier(jwtIssuer.issuerURI, oidc.NewRemoteKeySet(context.Background(), jwksURI), config)
393 } else {
394 } else {
394 verifier = provider.Verifier(config)
395 verifier = provider.Verifier(config)
providers/azure.go+12−37
@@ -3,10 +3,8 @@ package providers
3import (
3import (
4 "bytes"
4 "bytes"
5 "context"
5 "context"
6− "encoding/json"
7 "errors"
6 "errors"
8 "fmt"
7 "fmt"
9− "io/ioutil"
10 "net/http"
8 "net/http"
11 "net/url"
9 "net/url"
12 "time"
10 "time"
@@ -91,39 +89,21 @@ func (p *AzureProvider) Redeem(ctx context.Context, redirectURL, code string) (s
91 params.Add("resource", p.ProtectedResource.String())
89 params.Add("resource", p.ProtectedResource.String())
92 }
90 }
93
91
94− var req *http.Request
95− req, err = http.NewRequestWithContext(ctx, "POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
96− if err != nil {
97− return
98− }
99− req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
100−
101− var resp *http.Response
102− resp, err = http.DefaultClient.Do(req)
103− if err != nil {
104− return nil, err
105− }
106− var body []byte
107− body, err = ioutil.ReadAll(resp.Body)
108− resp.Body.Close()
109− if err != nil {
110− return
111− }
112−
113− if resp.StatusCode != 200 {
114− err = fmt.Errorf("got %d from %q %s", resp.StatusCode, p.RedeemURL.String(), body)
115− return
116− }
117−
118 var jsonResponse struct {
92 var jsonResponse struct {
119 AccessToken string `json:"access_token"`
93 AccessToken string `json:"access_token"`
120 RefreshToken string `json:"refresh_token"`
94 RefreshToken string `json:"refresh_token"`
121 ExpiresOn int64 `json:"expires_on,string"`
95 ExpiresOn int64 `json:"expires_on,string"`
122 IDToken string `json:"id_token"`
96 IDToken string `json:"id_token"`
123 }
97 }
98+
99+ err = requests.New(p.RedeemURL.String()).
100+ WithContext(ctx).
101+ WithMethod("POST").
102+ WithBody(bytes.NewBufferString(params.Encode())).
103+ SetHeader("Content-Type", "application/x-www-form-urlencoded").
124− err = json.Unmarshal(body, &jsonResponse)
104+ UnmarshalInto(&jsonResponse)
125 if err != nil {
105 if err != nil {
126− return
106+ return nil, err
127 }
107 }
128
108
129 created := time.Now()
109 created := time.Now()
@@ -169,26 +149,21 @@ func (p *AzureProvider) GetEmailAddress(ctx context.Context, s *sessions.Session
169 if s.AccessToken == "" {
149 if s.AccessToken == "" {
170 return "", errors.New("missing access token")
150 return "", errors.New("missing access token")
171 }
151 }
172− req, err := http.NewRequestWithContext(ctx, "GET", p.ProfileURL.String(), nil)
173− if err != nil {
174− return "", err
175− }
176− req.Header = getAzureHeader(s.AccessToken)
177−
178− json, err := requests.Request(req)
179
152
153+ json, err := requests.New(p.ProfileURL.String()).
154+ WithContext(ctx).
155+ WithHeaders(getAzureHeader(s.AccessToken)).
156+ UnmarshalJSON()
180 if err != nil {
157 if err != nil {
181 return "", err
158 return "", err
182 }
159 }
183
160
184 email, err = getEmailFromJSON(json)
161 email, err = getEmailFromJSON(json)
185−
186 if err == nil && email != "" {
162 if err == nil && email != "" {
187 return email, err
163 return email, err
188 }
164 }
189
165
190 email, err = json.Get("userPrincipalName").String()
166 email, err = json.Get("userPrincipalName").String()
191−
192 if err != nil {
167 if err != nil {
193 logger.Printf("failed making request %s", err)
168 logger.Printf("failed making request %s", err)
194 return "", err
169 return "", err
providers/bitbucket.go+23−28
@@ -2,7 +2,6 @@ package providers
2
2
3import (
3import (
4 "context"
4 "context"
5− "net/http"
6 "net/url"
5 "net/url"
7 "strings"
6 "strings"
8
7
@@ -85,15 +84,13 @@ func (p *BitbucketProvider) GetEmailAddress(ctx context.Context, s *sessions.Ses
85 FullName string `json:"full_name"`
84 FullName string `json:"full_name"`
86 }
85 }
87 }
86 }
88− req, err := http.NewRequestWithContext(ctx, "GET",
87+
89− p.ValidateURL.String()+"?access_token="+s.AccessToken, nil)
88+ requestURL := p.ValidateURL.String() + "?access_token=" + s.AccessToken
90− if err != nil {
89+ err := requests.New(requestURL).
91− logger.Printf("failed building request %s", err)
90+ WithContext(ctx).
92− return "", err
91+ UnmarshalInto(&emails)
93− }
94− err = requests.RequestJSON(req, &emails)
95 if err != nil {
92 if err != nil {
96− logger.Printf("failed making request %s", err)
93+ logger.Printf("failed making request: %v", err)
97 return "", err
94 return "", err
98 }
95 }
99
96
@@ -101,15 +98,14 @@ func (p *BitbucketProvider) GetEmailAddress(ctx context.Context, s *sessions.Ses
101 teamURL := &url.URL{}
98 teamURL := &url.URL{}
102 *teamURL = *p.ValidateURL
99 *teamURL = *p.ValidateURL
103 teamURL.Path = "/2.0/teams"
100 teamURL.Path = "/2.0/teams"
104− req, err = http.NewRequestWithContext(ctx, "GET",
101+
105− teamURL.String()+"?role=member&access_token="+s.AccessToken, nil)
102+ requestURL := teamURL.String() + "?role=member&access_token=" + s.AccessToken
106− if err != nil {
103+
107− logger.Printf("failed building request %s", err)
108− return "", err
109− }
110− err = requests.RequestJSON(req, &teams)
104+ err := requests.New(requestURL).
105+ WithContext(ctx).
106+ UnmarshalInto(&teams)
111 if err != nil {
107 if err != nil {
112− logger.Printf("failed requesting teams membership %s", err)
108+ logger.Printf("failed requesting teams membership: %v", err)
113 return "", err
109 return "", err
114 }
110 }
115 var found = false
111 var found = false
@@ -129,20 +125,19 @@ func (p *BitbucketProvider) GetEmailAddress(ctx context.Context, s *sessions.Ses
129 repositoriesURL := &url.URL{}
125 repositoriesURL := &url.URL{}
130 *repositoriesURL = *p.ValidateURL
126 *repositoriesURL = *p.ValidateURL
131 repositoriesURL.Path = "/2.0/repositories/" + strings.Split(p.Repository, "/")[0]
127 repositoriesURL.Path = "/2.0/repositories/" + strings.Split(p.Repository, "/")[0]
132− req, err = http.NewRequestWithContext(ctx, "GET",
128+
133− repositoriesURL.String()+"?role=contributor"+
129+ requestURL := repositoriesURL.String() + "?role=contributor" +
134− "&q=full_name="+url.QueryEscape("\""+p.Repository+"\"")+
130+ "&q=full_name=" + url.QueryEscape("\""+p.Repository+"\"") +
135− "&access_token="+s.AccessToken,
131+ "&access_token=" + s.AccessToken
136− nil)
132+
137− if err != nil {
133+ err := requests.New(requestURL).
138− logger.Printf("failed building request %s", err)
134+ WithContext(ctx).
139− return "", err
135+ UnmarshalInto(&repositories)
140− }
141− err = requests.RequestJSON(req, &repositories)
142 if err != nil {
136 if err != nil {
143− logger.Printf("failed checking repository access %s", err)
137+ logger.Printf("failed checking repository access: %v", err)
144 return "", err
138 return "", err
145 }
139 }
140+
146 var found = false
141 var found = false
147 for _, repository := range repositories.Values {
142 for _, repository := range repositories.Values {
148 if p.Repository == repository.FullName {
143 if p.Repository == repository.FullName {
providers/digitalocean.go+4−6
@@ -60,13 +60,11 @@ func (p *DigitalOceanProvider) GetEmailAddress(ctx context.Context, s *sessions.
60 if s.AccessToken == "" {
60 if s.AccessToken == "" {
61 return "", errors.New("missing access token")
61 return "", errors.New("missing access token")
62 }
62 }
63− req, err := http.NewRequestWithContext(ctx, "GET", p.ProfileURL.String(), nil)
64− if err != nil {
65− return "", err
66− }
67− req.Header = getDigitalOceanHeader(s.AccessToken)
68
63
69− json, err := requests.Request(req)
64+ json, err := requests.New(p.ProfileURL.String()).
65+ WithContext(ctx).
66+ WithHeaders(getDigitalOceanHeader(s.AccessToken)).
67+ UnmarshalJSON()
70 if err != nil {
68 if err != nil {
71 return "", err
69 return "", err
72 }
70 }
providers/facebook.go+7−6
@@ -62,20 +62,21 @@ func (p *FacebookProvider) GetEmailAddress(ctx context.Context, s *sessions.Sess
62 if s.AccessToken == "" {
62 if s.AccessToken == "" {
63 return "", errors.New("missing access token")
63 return "", errors.New("missing access token")
64 }
64 }
65− req, err := http.NewRequestWithContext(ctx, "GET", p.ProfileURL.String()+"?fields=name,email", nil)
66− if err != nil {
67− return "", err
68− }
69− req.Header = getFacebookHeader(s.AccessToken)
70
65
71 type result struct {
66 type result struct {
72 Email string
67 Email string
73 }
68 }
74 var r result
69 var r result
75− err = requests.RequestJSON(req, &r)
70+
71+ requestURL := p.ProfileURL.String() + "?fields=name,email"
72+ err := requests.New(requestURL).
73+ WithContext(ctx).
74+ WithHeaders(getFacebookHeader(s.AccessToken)).
75+ UnmarshalInto(&r)
76 if err != nil {
76 if err != nil {
77 return "", err
77 return "", err
78 }
78 }
79+
79 if r.Email == "" {
80 if r.Email == "" {
80 return "", errors.New("no email")
81 return "", errors.New("no email")
81 }
82 }
providers/github.go+37−114
@@ -2,7 +2,6 @@ package providers
2
2
3import (
3import (
4 "context"
4 "context"
5− "encoding/json"
6 "errors"
5 "errors"
7 "fmt"
6 "fmt"
8 "io/ioutil"
7 "io/ioutil"
@@ -15,6 +14,7 @@ import (
15
14
16 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
15 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
17 "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
16 "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
17+ "github.com/oauth2-proxy/oauth2-proxy/pkg/requests"
18)
18)
19
19
20// GitHubProvider represents an GitHub based Identity Provider
20// GitHubProvider represents an GitHub based Identity Provider
@@ -111,27 +111,16 @@ func (p *GitHubProvider) hasOrg(ctx context.Context, accessToken string) (bool,
111 Path: path.Join(p.ValidateURL.Path, "/user/orgs"),
111 Path: path.Join(p.ValidateURL.Path, "/user/orgs"),
112 RawQuery: params.Encode(),
112 RawQuery: params.Encode(),
113 }
113 }
114− req, _ := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil)
115− req.Header = getGitHubHeader(accessToken)
116− resp, err := http.DefaultClient.Do(req)
117− if err != nil {
118− return false, err
119− }
120
114
121− body, err := ioutil.ReadAll(resp.Body)
115+ var op orgsPage
122− resp.Body.Close()
116+ err := requests.New(endpoint.String()).
117+ WithContext(ctx).
118+ WithHeaders(getGitHubHeader(accessToken)).
119+ UnmarshalInto(&op)
123 if err != nil {
120 if err != nil {
124 return false, err
121 return false, err
125 }
122 }
126− if resp.StatusCode != 200 {
127− return false, fmt.Errorf(
128− "got %d from %q %s", resp.StatusCode, endpoint.String(), body)
129− }
130
123
131− var op orgsPage
132− if err := json.Unmarshal(body, &op); err != nil {
133− return false, err
134− }
135 if len(op) == 0 {
124 if len(op) == 0 {
136 break
125 break
137 }
126 }
@@ -187,9 +176,13 @@ func (p *GitHubProvider) hasOrgAndTeam(ctx context.Context, accessToken string)
187 RawQuery: params.Encode(),
176 RawQuery: params.Encode(),
188 }
177 }
189
178
190− req, _ := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil)
179+ // bodyclose cannot detect that the body is being closed later in requests.Into,
180+ // so have to skip the linting for the next line.
181+ // nolint:bodyclose
182+ resp, err := requests.New(endpoint.String()).
183+ WithContext(ctx).
191− req.Header = getGitHubHeader(accessToken)
184+ WithHeaders(getGitHubHeader(accessToken)).
192− resp, err := http.DefaultClient.Do(req)
185+ Do()
193 if err != nil {
186 if err != nil {
194 return false, err
187 return false, err
195 }
188 }
@@ -217,21 +210,9 @@ func (p *GitHubProvider) hasOrgAndTeam(ctx context.Context, accessToken string)
217 }
210 }
218 }
211 }
219
212
220− body, err := ioutil.ReadAll(resp.Body)
221− if err != nil {
222− resp.Body.Close()
223− return false, err
224− }
225− resp.Body.Close()
226−
227− if resp.StatusCode != 200 {
228− return false, fmt.Errorf(
229− "got %d from %q %s", resp.StatusCode, endpoint.String(), body)
230− }
231−
232 var tp teamsPage
213 var tp teamsPage
233− if err := json.Unmarshal(body, &tp); err != nil {
214+ if err := requests.UnmarshalInto(resp, &tp); err != nil {
234− return false, fmt.Errorf("%s unmarshaling %s", err, body)
215+ return false, err
235 }
216 }
236 if len(tp) == 0 {
217 if len(tp) == 0 {
237 break
218 break
@@ -297,25 +278,12 @@ func (p *GitHubProvider) hasRepo(ctx context.Context, accessToken string) (bool,
297 Path: path.Join(p.ValidateURL.Path, "/repo/", p.Repo),
278 Path: path.Join(p.ValidateURL.Path, "/repo/", p.Repo),
298 }
279 }
299
280
300− req, _ := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil)
301− req.Header = getGitHubHeader(accessToken)
302− resp, err := http.DefaultClient.Do(req)
303− if err != nil {
304− return false, err
305− }
306−
307− body, err := ioutil.ReadAll(resp.Body)
308− resp.Body.Close()
309− if err != nil {
310− return false, err
311− }
312− if resp.StatusCode != 200 {
313− return false, fmt.Errorf(
314− "got %d from %q %s", resp.StatusCode, endpoint.String(), body)
315− }
316−
317 var repo repository
281 var repo repository
318− if err := json.Unmarshal(body, &repo); err != nil {
282+ err := requests.New(endpoint.String()).
283+ WithContext(ctx).
284+ WithHeaders(getGitHubHeader(accessToken)).
285+ UnmarshalInto(&repo)
286+ if err != nil {
319 return false, err
287 return false, err
320 }
288 }
321
289
@@ -337,26 +305,14 @@ func (p *GitHubProvider) hasUser(ctx context.Context, accessToken string) (bool,
337 Host: p.ValidateURL.Host,
305 Host: p.ValidateURL.Host,
338 Path: path.Join(p.ValidateURL.Path, "/user"),
306 Path: path.Join(p.ValidateURL.Path, "/user"),
339 }
307 }
340− req, _ := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil)
341− req.Header = getGitHubHeader(accessToken)
342− resp, err := http.DefaultClient.Do(req)
343− if err != nil {
344− return false, err
345− }
346− defer resp.Body.Close()
347
308
348− body, err := ioutil.ReadAll(resp.Body)
309+ err := requests.New(endpoint.String()).
310+ WithContext(ctx).
311+ WithHeaders(getGitHubHeader(accessToken)).
312+ UnmarshalInto(&user)
349 if err != nil {
313 if err != nil {
350 return false, err
314 return false, err
351 }
315 }
352− if resp.StatusCode != 200 {
353− return false, fmt.Errorf("got %d from %q %s",
354− resp.StatusCode, stripToken(endpoint.String()), body)
355− }
356−
357− if err := json.Unmarshal(body, &user); err != nil {
358− return false, err
359− }
360
316
361 if p.isVerifiedUser(user.Login) {
317 if p.isVerifiedUser(user.Login) {
362 return true, nil
318 return true, nil
@@ -372,12 +328,14 @@ func (p *GitHubProvider) isCollaborator(ctx context.Context, username, accessTok
372 Host: p.ValidateURL.Host,
328 Host: p.ValidateURL.Host,
373 Path: path.Join(p.ValidateURL.Path, "/repos/", p.Repo, "/collaborators/", username),
329 Path: path.Join(p.ValidateURL.Path, "/repos/", p.Repo, "/collaborators/", username),
374 }
330 }
375− req, _ := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil)
331+ resp, err := requests.New(endpoint.String()).
332+ WithContext(ctx).
376− req.Header = getGitHubHeader(accessToken)
333+ WithHeaders(getGitHubHeader(accessToken)).
377− resp, err := http.DefaultClient.Do(req)
334+ Do()
378 if err != nil {
335 if err != nil {
379 return false, err
336 return false, err
380 }
337 }
338+
381 body, err := ioutil.ReadAll(resp.Body)
339 body, err := ioutil.ReadAll(resp.Body)
382 resp.Body.Close()
340 resp.Body.Close()
383 if err != nil {
341 if err != nil {
@@ -440,29 +398,14 @@ func (p *GitHubProvider) GetEmailAddress(ctx context.Context, s *sessions.Sessio
440 Host: p.ValidateURL.Host,
398 Host: p.ValidateURL.Host,
441 Path: path.Join(p.ValidateURL.Path, "/user/emails"),
399 Path: path.Join(p.ValidateURL.Path, "/user/emails"),
442 }
400 }
443− req, _ := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil)
401+ err := requests.New(endpoint.String()).
402+ WithContext(ctx).
444− req.Header = getGitHubHeader(s.AccessToken)
403+ WithHeaders(getGitHubHeader(s.AccessToken)).
445− resp, err := http.DefaultClient.Do(req)
404+ UnmarshalInto(&emails)
446− if err != nil {
447− return "", err
448− }
449− body, err := ioutil.ReadAll(resp.Body)
450− resp.Body.Close()
451 if err != nil {
405 if err != nil {
452 return "", err
406 return "", err
453 }
407 }
454
408
455− if resp.StatusCode != 200 {
456− return "", fmt.Errorf("got %d from %q %s",
457− resp.StatusCode, endpoint.String(), body)
458− }
459−
460− logger.Printf("got %d from %q %s", resp.StatusCode, endpoint.String(), body)
461−
462− if err := json.Unmarshal(body, &emails); err != nil {
463− return "", fmt.Errorf("%s unmarshaling %s", err, body)
464− }
465−
466 returnEmail := ""
409 returnEmail := ""
467 for _, email := range emails {
410 for _, email := range emails {
468 if email.Verified {
411 if email.Verified {
@@ -489,34 +432,14 @@ func (p *GitHubProvider) GetUserName(ctx context.Context, s *sessions.SessionSta
489 Path: path.Join(p.ValidateURL.Path, "/user"),
432 Path: path.Join(p.ValidateURL.Path, "/user"),
490 }
433 }
491
434
492− req, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil)
435+ err := requests.New(endpoint.String()).
493− if err != nil {
436+ WithContext(ctx).
494− return "", fmt.Errorf("could not create new GET request: %v", err)
495− }
496−
497− req.Header = getGitHubHeader(s.AccessToken)
437+ WithHeaders(getGitHubHeader(s.AccessToken)).
498− resp, err := http.DefaultClient.Do(req)
438+ UnmarshalInto(&user)
499− if err != nil {
500− return "", err
501− }
502−
503− body, err := ioutil.ReadAll(resp.Body)
504− defer resp.Body.Close()
505 if err != nil {
439 if err != nil {
506 return "", err
440 return "", err
507 }
441 }
508
442
509− if resp.StatusCode != 200 {
510− return "", fmt.Errorf("got %d from %q %s",
511− resp.StatusCode, endpoint.String(), body)
512− }
513−
514− logger.Printf("got %d from %q %s", resp.StatusCode, endpoint.String(), body)
515−
516− if err := json.Unmarshal(body, &user); err != nil {
517− return "", fmt.Errorf("%s unmarshaling %s", err, body)
518− }
519−
520 // Now that we have the username we can check collaborator status
443 // Now that we have the username we can check collaborator status
521 if !p.isVerifiedUser(user.Login) && p.Org == "" && p.Repo != "" && p.Token != "" {
444 if !p.isVerifiedUser(user.Login) && p.Org == "" && p.Repo != "" && p.Token != "" {
522 if ok, err := p.isCollaborator(ctx, user.Login, p.Token); err != nil || !ok {
445 if ok, err := p.isCollaborator(ctx, user.Login, p.Token); err != nil || !ok {
providers/gitlab.go+6−26
@@ -2,15 +2,13 @@ package providers
2
2
3import (
3import (
4 "context"
4 "context"
5− "encoding/json"
6 "fmt"
5 "fmt"
7− "io/ioutil"
8− "net/http"
9 "strings"
6 "strings"
10 "time"
7 "time"
11
8
12 oidc "github.com/coreos/go-oidc"
9 oidc "github.com/coreos/go-oidc"
13 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
10 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
11+ "github.com/oauth2-proxy/oauth2-proxy/pkg/requests"
14 "golang.org/x/oauth2"
12 "golang.org/x/oauth2"
15)
13)
16
14
@@ -131,31 +129,13 @@ func (p *GitLabProvider) getUserInfo(ctx context.Context, s *sessions.SessionSta
131 userInfoURL := *p.LoginURL
129 userInfoURL := *p.LoginURL
132 userInfoURL.Path = "/oauth/userinfo"
130 userInfoURL.Path = "/oauth/userinfo"
133
131
134− req, err := http.NewRequestWithContext(ctx, "GET", userInfoURL.String(), nil)
135− if err != nil {
136− return nil, fmt.Errorf("failed to create user info request: %v", err)
137− }
138− req.Header.Set("Authorization", "Bearer "+s.AccessToken)
139−
140− resp, err := http.DefaultClient.Do(req)
141− if err != nil {
142− return nil, fmt.Errorf("failed to perform user info request: %v", err)
143− }
144− var body []byte
145− body, err = ioutil.ReadAll(resp.Body)
146− resp.Body.Close()
147− if err != nil {
148− return nil, fmt.Errorf("failed to read user info response: %v", err)
149− }
150−
151− if resp.StatusCode != 200 {
152− return nil, fmt.Errorf("got %d during user info request: %s", resp.StatusCode, body)
153− }
154−
155 var userInfo gitlabUserInfo
132 var userInfo gitlabUserInfo
156− err = json.Unmarshal(body, &userInfo)
133+ err := requests.New(userInfoURL.String()).
134+ WithContext(ctx).
135+ SetHeader("Authorization", "Bearer "+s.AccessToken).
136+ UnmarshalInto(&userInfo)
157 if err != nil {
137 if err != nil {
158− return nil, fmt.Errorf("failed to parse user info: %v", err)
138+ return nil, fmt.Errorf("error getting user info: %v", err)
159 }
139 }
160
140
161 return &userInfo, nil
141 return &userInfo, nil
providers/google.go+19−49
@@ -9,13 +9,13 @@ import (
9 "fmt"
9 "fmt"
10 "io"
10 "io"
11 "io/ioutil"
11 "io/ioutil"
12− "net/http"
13 "net/url"
12 "net/url"
14 "strings"
13 "strings"
15 "time"
14 "time"
16
15
17 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
16 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
18 "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
17 "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
18+ "github.com/oauth2-proxy/oauth2-proxy/pkg/requests"
19 "golang.org/x/oauth2/google"
19 "golang.org/x/oauth2/google"
20 admin "google.golang.org/api/admin/directory/v1"
20 admin "google.golang.org/api/admin/directory/v1"
21 "google.golang.org/api/googleapi"
21 "google.golang.org/api/googleapi"
@@ -116,28 +116,6 @@ func (p *GoogleProvider) Redeem(ctx context.Context, redirectURL, code string) (
116 params.Add("client_secret", clientSecret)
116 params.Add("client_secret", clientSecret)
117 params.Add("code", code)
117 params.Add("code", code)
118 params.Add("grant_type", "authorization_code")
118 params.Add("grant_type", "authorization_code")
119− var req *http.Request
120− req, err = http.NewRequestWithContext(ctx, "POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
121− if err != nil {
122− return
123− }
124− req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
125−
126− resp, err := http.DefaultClient.Do(req)
127− if err != nil {
128− return
129− }
130− var body []byte
131− body, err = ioutil.ReadAll(resp.Body)
132− resp.Body.Close()
133− if err != nil {
134− return
135− }
136−
137− if resp.StatusCode != 200 {
138− err = fmt.Errorf("got %d from %q %s", resp.StatusCode, p.RedeemURL.String(), body)
139− return
140− }
141
119
142 var jsonResponse struct {
120 var jsonResponse struct {
143 AccessToken string `json:"access_token"`
121 AccessToken string `json:"access_token"`
@@ -145,10 +123,17 @@ func (p *GoogleProvider) Redeem(ctx context.Context, redirectURL, code string) (
145 ExpiresIn int64 `json:"expires_in"`
123 ExpiresIn int64 `json:"expires_in"`
146 IDToken string `json:"id_token"`
124 IDToken string `json:"id_token"`
147 }
125 }
126+
127+ err = requests.New(p.RedeemURL.String()).
128+ WithContext(ctx).
129+ WithMethod("POST").
130+ WithBody(bytes.NewBufferString(params.Encode())).
131+ SetHeader("Content-Type", "application/x-www-form-urlencoded").
148− err = json.Unmarshal(body, &jsonResponse)
132+ UnmarshalInto(&jsonResponse)
149 if err != nil {
133 if err != nil {
150− return
134+ return nil, err
151 }
135 }
136+
152 c, err := claimsFromIDToken(jsonResponse.IDToken)
137 c, err := claimsFromIDToken(jsonResponse.IDToken)
153 if err != nil {
138 if err != nil {
154 return
139 return
@@ -283,38 +268,23 @@ func (p *GoogleProvider) redeemRefreshToken(ctx context.Context, refreshToken st
283 params.Add("client_secret", clientSecret)
268 params.Add("client_secret", clientSecret)
284 params.Add("refresh_token", refreshToken)
269 params.Add("refresh_token", refreshToken)
285 params.Add("grant_type", "refresh_token")
270 params.Add("grant_type", "refresh_token")
286− var req *http.Request
287− req, err = http.NewRequestWithContext(ctx, "POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
288− if err != nil {
289− return
290− }
291− req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
292−
293− resp, err := http.DefaultClient.Do(req)
294− if err != nil {
295− return
296− }
297− var body []byte
298− body, err = ioutil.ReadAll(resp.Body)
299− resp.Body.Close()
300− if err != nil {
301− return
302− }
303−
304− if resp.StatusCode != 200 {
305− err = fmt.Errorf("got %d from %q %s", resp.StatusCode, p.RedeemURL.String(), body)
306− return
307− }
308
271
309 var data struct {
272 var data struct {
310 AccessToken string `json:"access_token"`
273 AccessToken string `json:"access_token"`
311 ExpiresIn int64 `json:"expires_in"`
274 ExpiresIn int64 `json:"expires_in"`
312 IDToken string `json:"id_token"`
275 IDToken string `json:"id_token"`
313 }
276 }
314− err = json.Unmarshal(body, &data)
277+
278+ err = requests.New(p.RedeemURL.String()).
279+ WithContext(ctx).
280+ WithMethod("POST").
281+ WithBody(bytes.NewBufferString(params.Encode())).
282+ SetHeader("Content-Type", "application/x-www-form-urlencoded").
283+ UnmarshalInto(&data)
315 if err != nil {
284 if err != nil {
316− return
285+ return "", "", 0, err
317 }
286 }
287+
318 token = data.AccessToken
288 token = data.AccessToken
319 idToken = data.IDToken
289 idToken = data.IDToken
320 expires = time.Duration(data.ExpiresIn) * time.Second
290 expires = time.Duration(data.ExpiresIn) * time.Second
providers/internal_util.go+5−1
@@ -56,7 +56,11 @@ func validateToken(ctx context.Context, p Provider, accessToken string, header h
56 params := url.Values{"access_token": {accessToken}}
56 params := url.Values{"access_token": {accessToken}}
57 endpoint = endpoint + "?" + params.Encode()
57 endpoint = endpoint + "?" + params.Encode()
58 }
58 }
59+
59− resp, err := requests.RequestUnparsedResponse(ctx, endpoint, header)
60+ resp, err := requests.New(endpoint).
61+ WithContext(ctx).
62+ WithHeaders(header).
63+ Do()
60 if err != nil {
64 if err != nil {
61 logger.Printf("GET %s", stripToken(endpoint))
65 logger.Printf("GET %s", stripToken(endpoint))
62 logger.Printf("token validation request failed: %s", err)
66 logger.Printf("token validation request failed: %s", err)
providers/keycloak.go+4−9
@@ -2,7 +2,6 @@ package providers
2
2
3import (
3import (
4 "context"
4 "context"
5− "net/http"
6 "net/url"
5 "net/url"
7
6
8 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
7 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
@@ -51,14 +50,10 @@ func (p *KeycloakProvider) SetGroup(group string) {
51}
50}
52
51
53func (p *KeycloakProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
52func (p *KeycloakProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
54−
55− req, err := http.NewRequestWithContext(ctx, "GET", p.ValidateURL.String(), nil)
53+ json, err := requests.New(p.ValidateURL.String()).
54+ WithContext(ctx).
56− req.Header.Set("Authorization", "Bearer "+s.AccessToken)
55+ SetHeader("Authorization", "Bearer "+s.AccessToken).
57− if err != nil {
56+ UnmarshalJSON()
58− logger.Printf("failed building request %s", err)
59− return "", err
60− }
61− json, err := requests.Request(req)
62 if err != nil {
57 if err != nil {
63 logger.Printf("failed making request %s", err)
58 logger.Printf("failed making request %s", err)
64 return "", err
59 return "", err
providers/linkedin.go+5−6
@@ -58,13 +58,12 @@ func (p *LinkedInProvider) GetEmailAddress(ctx context.Context, s *sessions.Sess
58 if s.AccessToken == "" {
58 if s.AccessToken == "" {
59 return "", errors.New("missing access token")
59 return "", errors.New("missing access token")
60 }
60 }
61− req, err := http.NewRequestWithContext(ctx, "GET", p.ProfileURL.String()+"?format=json", nil)
62− if err != nil {
63− return "", err
64− }
65− req.Header = getLinkedInHeader(s.AccessToken)
66
61
62+ requestURL := p.ProfileURL.String() + "?format=json"
67− json, err := requests.Request(req)
63+ json, err := requests.New(requestURL).
64+ WithContext(ctx).
65+ WithHeaders(getLinkedInHeader(s.AccessToken)).
66+ UnmarshalJSON()
68 if err != nil {
67 if err != nil {
69 return "", err
68 return "", err
70 }
69 }
providers/logingov.go+24−60
@@ -15,6 +15,7 @@ import (
15
15
16 "github.com/dgrijalva/jwt-go"
16 "github.com/dgrijalva/jwt-go"
17 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
17 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
18+ "github.com/oauth2-proxy/oauth2-proxy/pkg/requests"
18 "gopkg.in/square/go-jose.v2"
19 "gopkg.in/square/go-jose.v2"
19)
20)
20
21
@@ -128,51 +129,33 @@ func checkNonce(idToken string, p *LoginGovProvider) (err error) {
128 return
129 return
129}
130}
130
131
131−func emailFromUserInfo(ctx context.Context, accessToken string, userInfoEndpoint string) (email string, err error) {
132+func emailFromUserInfo(ctx context.Context, accessToken string, userInfoEndpoint string) (string, error) {
132− // query the user info endpoint for user attributes
133− var req *http.Request
134− req, err = http.NewRequestWithContext(ctx, "GET", userInfoEndpoint, nil)
135− if err != nil {
136− return
137− }
138− req.Header.Set("Authorization", "Bearer "+accessToken)
139−
140− resp, err := http.DefaultClient.Do(req)
141− if err != nil {
142− return
143− }
144− var body []byte
145− body, err = ioutil.ReadAll(resp.Body)
146− resp.Body.Close()
147− if err != nil {
148− return
149− }
150−
151− if resp.StatusCode != 200 {
152− err = fmt.Errorf("got %d from %q %s", resp.StatusCode, userInfoEndpoint, body)
153− return
154− }
155−
156 // parse the user attributes from the data we got and make sure that
133 // parse the user attributes from the data we got and make sure that
157 // the email address has been validated.
134 // the email address has been validated.
158 var emailData struct {
135 var emailData struct {
159 Email string `json:"email"`
136 Email string `json:"email"`
160 EmailVerified bool `json:"email_verified"`
137 EmailVerified bool `json:"email_verified"`
161 }
138 }
162− err = json.Unmarshal(body, &emailData)
139+
140+ // query the user info endpoint for user attributes
141+ err := requests.New(userInfoEndpoint).
142+ WithContext(ctx).
143+ SetHeader("Authorization", "Bearer "+accessToken).
144+ UnmarshalInto(&emailData)
163 if err != nil {
145 if err != nil {
164− return
146+ return "", err
165 }
147 }
148+
166− if emailData.Email == "" {
149+ email := emailData.Email
150+ if email == "" {
167− err = fmt.Errorf("missing email")
151+ return "", fmt.Errorf("missing email")
168− return
169 }
152 }
170− email = emailData.Email
153+
171 if !emailData.EmailVerified {
154 if !emailData.EmailVerified {
172− err = fmt.Errorf("email %s not listed as verified", email)
155+ return "", fmt.Errorf("email %s not listed as verified", email)
173− return
174 }
156 }
157+
175− return
158+ return email, nil
176}
159}
177
160
178// Redeem exchanges the OAuth2 authentication token for an ID token
161// Redeem exchanges the OAuth2 authentication token for an ID token
@@ -201,30 +184,6 @@ func (p *LoginGovProvider) Redeem(ctx context.Context, redirectURL, code string)
201 params.Add("code", code)
184 params.Add("code", code)
202 params.Add("grant_type", "authorization_code")
185 params.Add("grant_type", "authorization_code")
203
186
204− var req *http.Request
205− req, err = http.NewRequestWithContext(ctx, "POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
206− if err != nil {
207− return
208− }
209− req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
210−
211− var resp *http.Response
212− resp, err = http.DefaultClient.Do(req)
213− if err != nil {
214− return nil, err
215− }
216− var body []byte
217− body, err = ioutil.ReadAll(resp.Body)
218− resp.Body.Close()
219− if err != nil {
220− return
221− }
222−
223− if resp.StatusCode != 200 {
224− err = fmt.Errorf("got %d from %q %s", resp.StatusCode, p.RedeemURL.String(), body)
225− return
226− }
227−
228 // Get the token from the body that we got from the token endpoint.
187 // Get the token from the body that we got from the token endpoint.
229 var jsonResponse struct {
188 var jsonResponse struct {
230 AccessToken string `json:"access_token"`
189 AccessToken string `json:"access_token"`
@@ -232,9 +191,14 @@ func (p *LoginGovProvider) Redeem(ctx context.Context, redirectURL, code string)
232 TokenType string `json:"token_type"`
191 TokenType string `json:"token_type"`
233 ExpiresIn int64 `json:"expires_in"`
192 ExpiresIn int64 `json:"expires_in"`
234 }
193 }
194+ err = requests.New(p.RedeemURL.String()).
195+ WithContext(ctx).
196+ WithMethod("POST").
197+ WithBody(bytes.NewBufferString(params.Encode())).
198+ SetHeader("Content-Type", "application/x-www-form-urlencoded").
235− err = json.Unmarshal(body, &jsonResponse)
199+ UnmarshalInto(&jsonResponse)
236 if err != nil {
200 if err != nil {
237− return
201+ return nil, err
238 }
202 }
239
203
240 // check nonce here
204 // check nonce here
providers/nextcloud.go+6−11
@@ -6,7 +6,6 @@ import (
6 "net/http"
6 "net/http"
7
7
8 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
8 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
9− "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
10 "github.com/oauth2-proxy/oauth2-proxy/pkg/requests"
9 "github.com/oauth2-proxy/oauth2-proxy/pkg/requests"
11)
10)
12
11
@@ -31,18 +30,14 @@ func getNextcloudHeader(accessToken string) http.Header {
31
30
32// GetEmailAddress returns the Account email address
31// GetEmailAddress returns the Account email address
33func (p *NextcloudProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
32func (p *NextcloudProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
34− req, err := http.NewRequestWithContext(ctx, "GET",
35− p.ValidateURL.String(), nil)
33+ json, err := requests.New(p.ValidateURL.String()).
34+ WithContext(ctx).
35+ WithHeaders(getNextcloudHeader(s.AccessToken)).
36+ UnmarshalJSON()
36 if err != nil {
37 if err != nil {
37− logger.Printf("failed building request %s", err)
38− return "", err
39− }
40− req.Header = getNextcloudHeader(s.AccessToken)
41− json, err := requests.Request(req)
42− if err != nil {
43− logger.Printf("failed making request %s", err)
38+ return "", fmt.Errorf("error making request: %v", err)
44− return "", err
45 }
39 }
40+
46 email, err := json.Get("ocs").Get("data").Get("email").String()
41 email, err := json.Get("ocs").Get("data").Get("email").String()
47 return email, err
42 return email, err
48}
43}
providers/oidc.go+4−7
@@ -256,13 +256,10 @@ func (p *OIDCProvider) findClaimsFromIDToken(ctx context.Context, idToken *oidc.
256 // If the userinfo endpoint profileURL is defined, then there is a chance the userinfo
256 // If the userinfo endpoint profileURL is defined, then there is a chance the userinfo
257 // contents at the profileURL contains the email.
257 // contents at the profileURL contains the email.
258 // Make a query to the userinfo endpoint, and attempt to locate the email from there.
258 // Make a query to the userinfo endpoint, and attempt to locate the email from there.
259− req, err := http.NewRequestWithContext(ctx, "GET", profileURL, nil)
259+ respJSON, err := requests.New(profileURL).
260− if err != nil {
260+ WithContext(ctx).
261− return nil, err
262− }
263− req.Header = getOIDCHeader(accessToken)
261+ WithHeaders(getOIDCHeader(accessToken)).
264−
262+ UnmarshalJSON()
265− respJSON, err := requests.Request(req)
266 if err != nil {
263 if err != nil {
267 return nil, err
264 return nil, err
268 }
265 }
providers/provider_default.go+8−10
@@ -7,13 +7,13 @@ import (
7 "errors"
7 "errors"
8 "fmt"
8 "fmt"
9 "io/ioutil"
9 "io/ioutil"
10− "net/http"
11 "net/url"
10 "net/url"
12 "time"
11 "time"
13
12
14 "github.com/coreos/go-oidc"
13 "github.com/coreos/go-oidc"
15
14
16 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
15 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
16+ "github.com/oauth2-proxy/oauth2-proxy/pkg/requests"
17)
17)
18
18
19var _ Provider = (*ProviderData)(nil)
19var _ Provider = (*ProviderData)(nil)
@@ -39,18 +39,16 @@ func (p *ProviderData) Redeem(ctx context.Context, redirectURL, code string) (s
39 params.Add("resource", p.ProtectedResource.String())
39 params.Add("resource", p.ProtectedResource.String())
40 }
40 }
41
41
42− var req *http.Request
42+ resp, err := requests.New(p.RedeemURL.String()).
43+ WithContext(ctx).
44+ WithMethod("POST").
43− req, err = http.NewRequestWithContext(ctx, "POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
45+ WithBody(bytes.NewBufferString(params.Encode())).
44− if err != nil {
45− return
46− }
47− req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
46+ SetHeader("Content-Type", "application/x-www-form-urlencoded").
48−
47+ Do()
49− var resp *http.Response
50− resp, err = http.DefaultClient.Do(req)
51 if err != nil {
48 if err != nil {
52 return nil, err
49 return nil, err
53 }
50 }
51+
54 var body []byte
52 var body []byte
55 body, err = ioutil.ReadAll(resp.Body)
53 body, err = ioutil.ReadAll(resp.Body)
56 resp.Body.Close()
54 resp.Body.Close()