git.iko.soy/typeable/oauth2-proxy
on master help sign in
Support context in providers (#519)

Co-authored-by: Henry Jenkins <henry@henryjenkins.name>

commite642daef4efcf5199466ef9abe3b0f00ff7c614c authorMitsuo Heijo <mitsuo.heijo@gmail.com> dateWed 6 May 2020, 00:53 (6 years ago) parent53d8e99
33 files changed+223−173
CHANGELOG.md+1 oauthproxy.go+10−11 oauthproxy_test.go+4−2 pkg/requests/requests.go+3−2 pkg/requests/requests_test.go+4−3 providers/azure.go+7−4 providers/azure_test.go+8−7 providers/bitbucket.go+7−4 providers/bitbucket_test.go+5−4 providers/digitalocean.go+7−4 providers/digitalocean_test.go+4−3 providers/facebook.go+7−4 providers/github.go+15−12 providers/github_test.go+7−6 providers/gitlab.go+13−14 providers/gitlab_test.go+9−8 providers/google.go+8−6 providers/google_test.go+5−5 providers/internal_util.go+3−2 providers/internal_util_test.go+11−8 providers/keycloak.go+5−2 providers/keycloak_test.go+5−4 providers/linkedin.go+7−4 providers/linkedin_test.go+4−3 providers/logingov.go+8−5 providers/logingov_test.go+3−2 providers/nextcloud.go+5−2 providers/nextcloud_test.go+4−3 providers/oidc.go+17−18 providers/oidc_test.go+4−4 providers/provider_default.go+12−9 providers/provider_default_test.go+2−1 providers/providers.go+9−7
CHANGELOG.md+1
@@ -33,6 +33,7 @@
3333- [#514](https://github.com/oauth2-proxy/oauth2-proxy/pull/514) Add basic string functions to templates
3434- [#524](https://github.com/oauth2-proxy/oauth2-proxy/pull/524) Sign cookies with SHA256 (@NickMeves)
3535- [#515](https://github.com/oauth2-proxy/oauth2-proxy/pull/515) Drop configure script in favour of native Makefile env and checks (@JoelSpeed)
36+- [#519](https://github.com/oauth2-proxy/oauth2-proxy/pull/519) Support context in providers (@johejo)
3637- [#487](https://github.com/oauth2-proxy/oauth2-proxy/pull/487) Switch flags to PFlag to remove StringArray (@JoelSpeed)
3738- [#484](https://github.com/oauth2-proxy/oauth2-proxy/pull/484) Replace configuration loading with Viper (@JoelSpeed)
3839- [#499](https://github.com/oauth2-proxy/oauth2-proxy/pull/499) Add `-user-id-claim` to support generic claims in addition to email (@holyjak)
oauthproxy.go+10−11
@@ -347,29 +347,29 @@ func (p *OAuthProxy) displayCustomLoginForm() bool {
347347 return p.HtpasswdFile != nil && p.DisplayHtpasswdForm
348348}
349349
350−func (p *OAuthProxy) redeemCode(host, code string) (s *sessionsapi.SessionState, err error) {
350+func (p *OAuthProxy) redeemCode(ctx context.Context, host, code string) (s *sessionsapi.SessionState, err error) {
351351 if code == "" {
352352 return nil, errors.New("missing code")
353353 }
354354 redirectURI := p.GetRedirectURI(host)
355− s, err = p.provider.Redeem(redirectURI, code)
355+ s, err = p.provider.Redeem(ctx, redirectURI, code)
356356 if err != nil {
357357 return
358358 }
359359
360360 if s.Email == "" {
361− s.Email, err = p.provider.GetEmailAddress(s)
361+ s.Email, err = p.provider.GetEmailAddress(ctx, s)
362362 }
363363
364364 if s.PreferredUsername == "" {
365− s.PreferredUsername, err = p.provider.GetPreferredUsername(s)
365+ s.PreferredUsername, err = p.provider.GetPreferredUsername(ctx, s)
366366 if err != nil && err.Error() == "not implemented" {
367367 err = nil
368368 }
369369 }
370370
371371 if s.User == "" {
372− s.User, err = p.provider.GetUserName(s)
372+ s.User, err = p.provider.GetUserName(ctx, s)
373373 if err != nil && err.Error() == "not implemented" {
374374 err = nil
375375 }
@@ -782,7 +782,7 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
782782 return
783783 }
784784
785− session, err := p.redeemCode(req.Host, req.Form.Get("code"))
785+ session, err := p.redeemCode(req.Context(), req.Host, req.Form.Get("code"))
786786 if err != nil {
787787 logger.Printf("Error redeeming code during OAuth2 callback: %s ", err.Error())
788788 p.ErrorPage(rw, 500, "Internal Error", "Internal Error")
@@ -907,7 +907,7 @@ func (p *OAuthProxy) getAuthenticatedSession(rw http.ResponseWriter, req *http.R
907907 saveSession = true
908908 }
909909
910− if ok, err := p.provider.RefreshSessionIfNeeded(session); err != nil {
910+ if ok, err := p.provider.RefreshSessionIfNeeded(req.Context(), session); err != nil {
911911 logger.Printf("%s removing session. error refreshing access token %s %s", remoteAddr, err, session)
912912 clearSession = true
913913 session = nil
@@ -926,7 +926,7 @@ func (p *OAuthProxy) getAuthenticatedSession(rw http.ResponseWriter, req *http.R
926926 }
927927
928928 if saveSession && !revalidated && session != nil && session.AccessToken != "" {
929− if !p.provider.ValidateSessionState(session) {
929+ if !p.provider.ValidateSessionState(req.Context(), session) {
930930 logger.Printf("Removing session: error validating %s", session)
931931 saveSession = false
932932 session = nil
@@ -1126,16 +1126,15 @@ func (p *OAuthProxy) GetJwtSession(req *http.Request) (*sessionsapi.SessionState
11261126 return nil, err
11271127 }
11281128
1129− ctx := context.Background()
11301129 for _, verifier := range p.jwtBearerVerifiers {
1131− bearerToken, err := verifier.Verify(ctx, rawBearerToken)
1130+ bearerToken, err := verifier.Verify(req.Context(), rawBearerToken)
11321131
11331132 if err != nil {
11341133 logger.Printf("failed to verify bearer token: %v", err)
11351134 continue
11361135 }
11371136
1138− return p.provider.CreateSessionStateFromBearerToken(rawBearerToken, bearerToken)
1137+ return p.provider.CreateSessionStateFromBearerToken(req.Context(), rawBearerToken, bearerToken)
11391138 }
11401139 return nil, fmt.Errorf("unable to verify jwt token %s", req.Header.Get("Authorization"))
11411140}
oauthproxy_test.go+4−2
@@ -397,6 +397,8 @@ type TestProvider struct {
397397 GroupValidator func(string) bool
398398}
399399
400+var _ providers.Provider = (*TestProvider)(nil)
401+
400402func NewTestProvider(providerURL *url.URL, emailAddress string) *TestProvider {
401403 return &TestProvider{
402404 ProviderData: &providers.ProviderData{
@@ -425,11 +427,11 @@ func NewTestProvider(providerURL *url.URL, emailAddress string) *TestProvider {
425427 }
426428}
427429
428−func (tp *TestProvider) GetEmailAddress(session *sessions.SessionState) (string, error) {
430+func (tp *TestProvider) GetEmailAddress(ctx context.Context, session *sessions.SessionState) (string, error) {
429431 return tp.EmailAddress, nil
430432}
431433
432−func (tp *TestProvider) ValidateSessionState(session *sessions.SessionState) bool {
434+func (tp *TestProvider) ValidateSessionState(ctx context.Context, session *sessions.SessionState) bool {
433435 return tp.ValidToken
434436}
435437
pkg/requests/requests.go+3−2
@@ -1,6 +1,7 @@
11package requests
22
33import (
4+ "context"
45 "encoding/json"
56 "fmt"
67 "io/ioutil"
@@ -62,8 +63,8 @@ func RequestJSON(req *http.Request, v interface{}) error {
6263}
6364
6465// RequestUnparsedResponse performs a GET and returns the raw response object
65−func RequestUnparsedResponse(url string, header http.Header) (resp *http.Response, err error) {
66− req, err := http.NewRequest("GET", url, nil)
66+func RequestUnparsedResponse(ctx context.Context, url string, header http.Header) (resp *http.Response, err error) {
67+ req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
6768 if err != nil {
6869 return nil, fmt.Errorf("error performing get request: %w", err)
6970 }
pkg/requests/requests_test.go+4−3
@@ -1,6 +1,7 @@
11package requests
22
33import (
4+ "context"
45 "io/ioutil"
56 "net/http"
67 "net/http/httptest"
@@ -87,7 +88,7 @@ func TestRequestUnparsedResponseUsingAccessTokenParameter(t *testing.T) {
8788 defer backend.Close()
8889
8990 response, err := RequestUnparsedResponse(
90− backend.URL+"?access_token=my_token", nil)
91+ context.Background(), backend.URL+"?access_token=my_token", nil)
9192 assert.Equal(t, nil, err)
9293 defer response.Body.Close()
9394
@@ -103,7 +104,7 @@ func TestRequestUnparsedResponseUsingAccessTokenParameterFailedResponse(t *testi
103104 backend.Close()
104105
105106 response, err := RequestUnparsedResponse(
106− backend.URL+"?access_token=my_token", nil)
107+ context.Background(), backend.URL+"?access_token=my_token", nil)
107108 assert.NotEqual(t, nil, err)
108109 assert.Equal(t, (*http.Response)(nil), response)
109110}
@@ -123,7 +124,7 @@ func TestRequestUnparsedResponseUsingHeaders(t *testing.T) {
123124
124125 headers := make(http.Header)
125126 headers.Set("Auth", "my_token")
126− response, err := RequestUnparsedResponse(backend.URL, headers)
127+ response, err := RequestUnparsedResponse(context.Background(), backend.URL, headers)
127128 assert.Equal(t, nil, err)
128129 defer response.Body.Close()
129130
providers/azure.go+7−4
@@ -2,6 +2,7 @@ package providers
22
33import (
44 "bytes"
5+ "context"
56 "encoding/json"
67 "errors"
78 "fmt"
@@ -22,6 +23,8 @@ type AzureProvider struct {
2223 Tenant string
2324}
2425
26+var _ Provider = (*AzureProvider)(nil)
27+
2528// NewAzureProvider initiates a new AzureProvider
2629func NewAzureProvider(p *ProviderData) *AzureProvider {
2730 p.ProviderName = "Azure"
@@ -68,7 +71,7 @@ func (p *AzureProvider) Configure(tenant string) {
6871 }
6972}
7073
71−func (p *AzureProvider) Redeem(redirectURL, code string) (s *sessions.SessionState, err error) {
74+func (p *AzureProvider) Redeem(ctx context.Context, redirectURL, code string) (s *sessions.SessionState, err error) {
7275 if code == "" {
7376 err = errors.New("missing code")
7477 return
@@ -89,7 +92,7 @@ func (p *AzureProvider) Redeem(redirectURL, code string) (s *sessions.SessionSta
8992 }
9093
9194 var req *http.Request
92− req, err = http.NewRequest("POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
95+ req, err = http.NewRequestWithContext(ctx, "POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
9396 if err != nil {
9497 return
9598 }
@@ -157,14 +160,14 @@ func getEmailFromJSON(json *simplejson.Json) (string, error) {
157160}
158161
159162// GetEmailAddress returns the Account email address
160−func (p *AzureProvider) GetEmailAddress(s *sessions.SessionState) (string, error) {
163+func (p *AzureProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
161164 var email string
162165 var err error
163166
164167 if s.AccessToken == "" {
165168 return "", errors.New("missing access token")
166169 }
167− req, err := http.NewRequest("GET", p.ProfileURL.String(), nil)
170+ req, err := http.NewRequestWithContext(ctx, "GET", p.ProfileURL.String(), nil)
168171 if err != nil {
169172 return "", err
170173 }
providers/azure_test.go+8−7
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "net/http"
56 "net/http/httptest"
67 "net/url"
@@ -133,7 +134,7 @@ func TestAzureProviderGetEmailAddress(t *testing.T) {
133134 p := testAzureProvider(bURL.Host)
134135
135136 session := CreateAuthorizedSession()
136− email, err := p.GetEmailAddress(session)
137+ email, err := p.GetEmailAddress(context.Background(), session)
137138 assert.Equal(t, nil, err)
138139 assert.Equal(t, "user@windows.net", email)
139140}
@@ -146,7 +147,7 @@ func TestAzureProviderGetEmailAddressMailNull(t *testing.T) {
146147 p := testAzureProvider(bURL.Host)
147148
148149 session := CreateAuthorizedSession()
149− email, err := p.GetEmailAddress(session)
150+ email, err := p.GetEmailAddress(context.Background(), session)
150151 assert.Equal(t, nil, err)
151152 assert.Equal(t, "user@windows.net", email)
152153}
@@ -159,7 +160,7 @@ func TestAzureProviderGetEmailAddressGetUserPrincipalName(t *testing.T) {
159160 p := testAzureProvider(bURL.Host)
160161
161162 session := CreateAuthorizedSession()
162− email, err := p.GetEmailAddress(session)
163+ email, err := p.GetEmailAddress(context.Background(), session)
163164 assert.Equal(t, nil, err)
164165 assert.Equal(t, "user@windows.net", email)
165166}
@@ -172,7 +173,7 @@ func TestAzureProviderGetEmailAddressFailToGetEmailAddress(t *testing.T) {
172173 p := testAzureProvider(bURL.Host)
173174
174175 session := CreateAuthorizedSession()
175− email, err := p.GetEmailAddress(session)
176+ email, err := p.GetEmailAddress(context.Background(), session)
176177 assert.Equal(t, "type assertion to string failed", err.Error())
177178 assert.Equal(t, "", email)
178179}
@@ -185,7 +186,7 @@ func TestAzureProviderGetEmailAddressEmptyUserPrincipalName(t *testing.T) {
185186 p := testAzureProvider(bURL.Host)
186187
187188 session := CreateAuthorizedSession()
188− email, err := p.GetEmailAddress(session)
189+ email, err := p.GetEmailAddress(context.Background(), session)
189190 assert.Equal(t, nil, err)
190191 assert.Equal(t, "", email)
191192}
@@ -198,7 +199,7 @@ func TestAzureProviderGetEmailAddressIncorrectOtherMails(t *testing.T) {
198199 p := testAzureProvider(bURL.Host)
199200
200201 session := CreateAuthorizedSession()
201− email, err := p.GetEmailAddress(session)
202+ email, err := p.GetEmailAddress(context.Background(), session)
202203 assert.Equal(t, "type assertion to string failed", err.Error())
203204 assert.Equal(t, "", email)
204205}
@@ -212,7 +213,7 @@ func TestAzureProviderRedeemReturnsIdToken(t *testing.T) {
212213 bURL, _ := url.Parse(b.URL)
213214 p := testAzureProvider(bURL.Host)
214215 p.Data().RedeemURL.Path = "/common/oauth2/token"
215− s, err := p.Redeem("https://localhost", "1234")
216+ s, err := p.Redeem(context.Background(), "https://localhost", "1234")
216217 assert.Equal(t, nil, err)
217218 assert.Equal(t, "testtoken1234", s.IDToken)
218219 assert.Equal(t, timestamp, s.ExpiresOn.UTC())
providers/bitbucket.go+7−4
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "net/http"
56 "net/url"
67 "strings"
@@ -17,6 +18,8 @@ type BitbucketProvider struct {
1718 Repository string
1819}
1920
21+var _ Provider = (*BitbucketProvider)(nil)
22+
2023// NewBitbucketProvider initiates a new BitbucketProvider
2124func NewBitbucketProvider(p *ProviderData) *BitbucketProvider {
2225 p.ProviderName = "Bitbucket"
@@ -64,7 +67,7 @@ func (p *BitbucketProvider) SetRepository(repository string) {
6467}
6568
6669// GetEmailAddress returns the email of the authenticated user
67−func (p *BitbucketProvider) GetEmailAddress(s *sessions.SessionState) (string, error) {
70+func (p *BitbucketProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
6871
6972 var emails struct {
7073 Values []struct {
@@ -82,7 +85,7 @@ func (p *BitbucketProvider) GetEmailAddress(s *sessions.SessionState) (string, e
8285 FullName string `json:"full_name"`
8386 }
8487 }
85− req, err := http.NewRequest("GET",
88+ req, err := http.NewRequestWithContext(ctx, "GET",
8689 p.ValidateURL.String()+"?access_token="+s.AccessToken, nil)
8790 if err != nil {
8891 logger.Printf("failed building request %s", err)
@@ -98,7 +101,7 @@ func (p *BitbucketProvider) GetEmailAddress(s *sessions.SessionState) (string, e
98101 teamURL := &url.URL{}
99102 *teamURL = *p.ValidateURL
100103 teamURL.Path = "/2.0/teams"
101− req, err = http.NewRequest("GET",
104+ req, err = http.NewRequestWithContext(ctx, "GET",
102105 teamURL.String()+"?role=member&access_token="+s.AccessToken, nil)
103106 if err != nil {
104107 logger.Printf("failed building request %s", err)
@@ -126,7 +129,7 @@ func (p *BitbucketProvider) GetEmailAddress(s *sessions.SessionState) (string, e
126129 repositoriesURL := &url.URL{}
127130 *repositoriesURL = *p.ValidateURL
128131 repositoriesURL.Path = "/2.0/repositories/" + strings.Split(p.Repository, "/")[0]
129− req, err = http.NewRequest("GET",
132+ req, err = http.NewRequestWithContext(ctx, "GET",
130133 repositoriesURL.String()+"?role=contributor"+
131134 "&q=full_name="+url.QueryEscape("\""+p.Repository+"\"")+
132135 "&access_token="+s.AccessToken,
providers/bitbucket_test.go+5−4
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "log"
56 "net/http"
67 "net/http/httptest"
@@ -120,7 +121,7 @@ func TestBitbucketProviderGetEmailAddress(t *testing.T) {
120121 p := testBitbucketProvider(bURL.Host, "", "")
121122
122123 session := CreateAuthorizedSession()
123− email, err := p.GetEmailAddress(session)
124+ email, err := p.GetEmailAddress(context.Background(), session)
124125 assert.Equal(t, nil, err)
125126 assert.Equal(t, "michael.bland@gsa.gov", email)
126127}
@@ -133,7 +134,7 @@ func TestBitbucketProviderGetEmailAddressAndGroup(t *testing.T) {
133134 p := testBitbucketProvider(bURL.Host, "bioinformatics", "")
134135
135136 session := CreateAuthorizedSession()
136− email, err := p.GetEmailAddress(session)
137+ email, err := p.GetEmailAddress(context.Background(), session)
137138 assert.Equal(t, nil, err)
138139 assert.Equal(t, "michael.bland@gsa.gov", email)
139140}
@@ -151,7 +152,7 @@ func TestBitbucketProviderGetEmailAddressFailedRequest(t *testing.T) {
151152 // token. Alternatively, we could allow the parsing of the payload as
152153 // JSON to fail.
153154 session := &sessions.SessionState{AccessToken: "unexpected_access_token"}
154− email, err := p.GetEmailAddress(session)
155+ email, err := p.GetEmailAddress(context.Background(), session)
155156 assert.NotEqual(t, nil, err)
156157 assert.Equal(t, "", email)
157158}
@@ -164,7 +165,7 @@ func TestBitbucketProviderGetEmailAddressEmailNotPresentInPayload(t *testing.T)
164165 p := testBitbucketProvider(bURL.Host, "", "")
165166
166167 session := CreateAuthorizedSession()
167− email, err := p.GetEmailAddress(session)
168+ email, err := p.GetEmailAddress(context.Background(), session)
168169 assert.Equal(t, "", email)
169170 assert.Equal(t, nil, err)
170171}
providers/digitalocean.go+7−4
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "errors"
56 "fmt"
67 "net/http"
@@ -15,6 +16,8 @@ type DigitalOceanProvider struct {
1516 *ProviderData
1617}
1718
19+var _ Provider = (*DigitalOceanProvider)(nil)
20+
1821// NewDigitalOceanProvider initiates a new DigitalOceanProvider
1922func NewDigitalOceanProvider(p *ProviderData) *DigitalOceanProvider {
2023 p.ProviderName = "DigitalOcean"
@@ -53,11 +56,11 @@ func getDigitalOceanHeader(accessToken string) http.Header {
5356}
5457
5558// GetEmailAddress returns the Account email address
56−func (p *DigitalOceanProvider) GetEmailAddress(s *sessions.SessionState) (string, error) {
59+func (p *DigitalOceanProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
5760 if s.AccessToken == "" {
5861 return "", errors.New("missing access token")
5962 }
60− req, err := http.NewRequest("GET", p.ProfileURL.String(), nil)
63+ req, err := http.NewRequestWithContext(ctx, "GET", p.ProfileURL.String(), nil)
6164 if err != nil {
6265 return "", err
6366 }
@@ -76,6 +79,6 @@ func (p *DigitalOceanProvider) GetEmailAddress(s *sessions.SessionState) (string
7679}
7780
7881// ValidateSessionState validates the AccessToken
79−func (p *DigitalOceanProvider) ValidateSessionState(s *sessions.SessionState) bool {
80− return validateToken(p, s.AccessToken, getDigitalOceanHeader(s.AccessToken))
82+func (p *DigitalOceanProvider) ValidateSessionState(ctx context.Context, s *sessions.SessionState) bool {
83+ return validateToken(ctx, p, s.AccessToken, getDigitalOceanHeader(s.AccessToken))
8184}
providers/digitalocean_test.go+4−3
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "net/http"
56 "net/http/httptest"
67 "net/url"
@@ -99,7 +100,7 @@ func TestDigitalOceanProviderGetEmailAddress(t *testing.T) {
99100 p := testDigitalOceanProvider(bURL.Host)
100101
101102 session := CreateAuthorizedSession()
102− email, err := p.GetEmailAddress(session)
103+ email, err := p.GetEmailAddress(context.Background(), session)
103104 assert.Equal(t, nil, err)
104105 assert.Equal(t, "user@example.com", email)
105106}
@@ -115,7 +116,7 @@ func TestDigitalOceanProviderGetEmailAddressFailedRequest(t *testing.T) {
115116 // token. Alternatively, we could allow the parsing of the payload as
116117 // JSON to fail.
117118 session := &sessions.SessionState{AccessToken: "unexpected_access_token"}
118− email, err := p.GetEmailAddress(session)
119+ email, err := p.GetEmailAddress(context.Background(), session)
119120 assert.NotEqual(t, nil, err)
120121 assert.Equal(t, "", email)
121122}
@@ -128,7 +129,7 @@ func TestDigitalOceanProviderGetEmailAddressEmailNotPresentInPayload(t *testing.
128129 p := testDigitalOceanProvider(bURL.Host)
129130
130131 session := CreateAuthorizedSession()
131− email, err := p.GetEmailAddress(session)
132+ email, err := p.GetEmailAddress(context.Background(), session)
132133 assert.NotEqual(t, nil, err)
133134 assert.Equal(t, "", email)
134135}
providers/facebook.go+7−4
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "errors"
56 "fmt"
67 "net/http"
@@ -15,6 +16,8 @@ type FacebookProvider struct {
1516 *ProviderData
1617}
1718
19+var _ Provider = (*FacebookProvider)(nil)
20+
1821// NewFacebookProvider initiates a new FacebookProvider
1922func NewFacebookProvider(p *ProviderData) *FacebookProvider {
2023 p.ProviderName = "Facebook"
@@ -55,11 +58,11 @@ func getFacebookHeader(accessToken string) http.Header {
5558}
5659
5760// GetEmailAddress returns the Account email address
58−func (p *FacebookProvider) GetEmailAddress(s *sessions.SessionState) (string, error) {
61+func (p *FacebookProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
5962 if s.AccessToken == "" {
6063 return "", errors.New("missing access token")
6164 }
62− req, err := http.NewRequest("GET", p.ProfileURL.String()+"?fields=name,email", nil)
65+ req, err := http.NewRequestWithContext(ctx, "GET", p.ProfileURL.String()+"?fields=name,email", nil)
6366 if err != nil {
6467 return "", err
6568 }
@@ -80,6 +83,6 @@ func (p *FacebookProvider) GetEmailAddress(s *sessions.SessionState) (string, er
8083}
8184
8285// ValidateSessionState validates the AccessToken
83−func (p *FacebookProvider) ValidateSessionState(s *sessions.SessionState) bool {
84− return validateToken(p, s.AccessToken, getFacebookHeader(s.AccessToken))
86+func (p *FacebookProvider) ValidateSessionState(ctx context.Context, s *sessions.SessionState) bool {
87+ return validateToken(ctx, p, s.AccessToken, getFacebookHeader(s.AccessToken))
8588}
providers/github.go+15−12
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "encoding/json"
56 "fmt"
67 "io/ioutil"
@@ -22,6 +23,8 @@ type GitHubProvider struct {
2223 Team string
2324}
2425
26+var _ Provider = (*GitHubProvider)(nil)
27+
2528// NewGitHubProvider initiates a new GitHubProvider
2629func NewGitHubProvider(p *ProviderData) *GitHubProvider {
2730 p.ProviderName = "GitHub"
@@ -69,7 +72,7 @@ func (p *GitHubProvider) SetOrgTeam(org, team string) {
6972 }
7073}
7174
72−func (p *GitHubProvider) hasOrg(accessToken string) (bool, error) {
75+func (p *GitHubProvider) hasOrg(ctx context.Context, accessToken string) (bool, error) {
7376 // https://developer.github.com/v3/orgs/#list-your-organizations
7477
7578 var orgs []struct {
@@ -93,7 +96,7 @@ func (p *GitHubProvider) hasOrg(accessToken string) (bool, error) {
9396 Path: path.Join(p.ValidateURL.Path, "/user/orgs"),
9497 RawQuery: params.Encode(),
9598 }
96− req, _ := http.NewRequest("GET", endpoint.String(), nil)
99+ req, _ := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil)
97100 req.Header = getGitHubHeader(accessToken)
98101 resp, err := http.DefaultClient.Do(req)
99102 if err != nil {
@@ -135,7 +138,7 @@ func (p *GitHubProvider) hasOrg(accessToken string) (bool, error) {
135138 return false, nil
136139}
137140
138−func (p *GitHubProvider) hasOrgAndTeam(accessToken string) (bool, error) {
141+func (p *GitHubProvider) hasOrgAndTeam(ctx context.Context, accessToken string) (bool, error) {
139142 // https://developer.github.com/v3/orgs/teams/#list-user-teams
140143
141144 var teams []struct {
@@ -169,7 +172,7 @@ func (p *GitHubProvider) hasOrgAndTeam(accessToken string) (bool, error) {
169172 RawQuery: params.Encode(),
170173 }
171174
172− req, _ := http.NewRequest("GET", endpoint.String(), nil)
175+ req, _ := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil)
173176 req.Header = getGitHubHeader(accessToken)
174177 resp, err := http.DefaultClient.Do(req)
175178 if err != nil {
@@ -261,7 +264,7 @@ func (p *GitHubProvider) hasOrgAndTeam(accessToken string) (bool, error) {
261264}
262265
263266// GetEmailAddress returns the Account email address
264−func (p *GitHubProvider) GetEmailAddress(s *sessions.SessionState) (string, error) {
267+func (p *GitHubProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
265268
266269 var emails []struct {
267270 Email string `json:"email"`
@@ -272,11 +275,11 @@ func (p *GitHubProvider) GetEmailAddress(s *sessions.SessionState) (string, erro
272275 // if we require an Org or Team, check that first
273276 if p.Org != "" {
274277 if p.Team != "" {
275− if ok, err := p.hasOrgAndTeam(s.AccessToken); err != nil || !ok {
278+ if ok, err := p.hasOrgAndTeam(ctx, s.AccessToken); err != nil || !ok {
276279 return "", err
277280 }
278281 } else {
279− if ok, err := p.hasOrg(s.AccessToken); err != nil || !ok {
282+ if ok, err := p.hasOrg(ctx, s.AccessToken); err != nil || !ok {
280283 return "", err
281284 }
282285 }
@@ -287,7 +290,7 @@ func (p *GitHubProvider) GetEmailAddress(s *sessions.SessionState) (string, erro
287290 Host: p.ValidateURL.Host,
288291 Path: path.Join(p.ValidateURL.Path, "/user/emails"),
289292 }
290− req, _ := http.NewRequest("GET", endpoint.String(), nil)
293+ req, _ := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil)
291294 req.Header = getGitHubHeader(s.AccessToken)
292295 resp, err := http.DefaultClient.Do(req)
293296 if err != nil {
@@ -324,7 +327,7 @@ func (p *GitHubProvider) GetEmailAddress(s *sessions.SessionState) (string, erro
324327}
325328
326329// GetUserName returns the Account user name
327−func (p *GitHubProvider) GetUserName(s *sessions.SessionState) (string, error) {
330+func (p *GitHubProvider) GetUserName(ctx context.Context, s *sessions.SessionState) (string, error) {
328331 var user struct {
329332 Login string `json:"login"`
330333 Email string `json:"email"`
@@ -336,7 +339,7 @@ func (p *GitHubProvider) GetUserName(s *sessions.SessionState) (string, error) {
336339 Path: path.Join(p.ValidateURL.Path, "/user"),
337340 }
338341
339− req, err := http.NewRequest("GET", endpoint.String(), nil)
342+ req, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil)
340343 if err != nil {
341344 return "", fmt.Errorf("could not create new GET request: %v", err)
342345 }
@@ -368,6 +371,6 @@ func (p *GitHubProvider) GetUserName(s *sessions.SessionState) (string, error) {
368371}
369372
370373// ValidateSessionState validates the AccessToken
371−func (p *GitHubProvider) ValidateSessionState(s *sessions.SessionState) bool {
372− return validateToken(p, s.AccessToken, getGitHubHeader(s.AccessToken))
374+func (p *GitHubProvider) ValidateSessionState(ctx context.Context, s *sessions.SessionState) bool {
375+ return validateToken(ctx, p, s.AccessToken, getGitHubHeader(s.AccessToken))
373376}
providers/github_test.go+7−6
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "net/http"
56 "net/http/httptest"
67 "net/url"
@@ -105,7 +106,7 @@ func TestGitHubProviderGetEmailAddress(t *testing.T) {
105106 p := testGitHubProvider(bURL.Host)
106107
107108 session := CreateAuthorizedSession()
108− email, err := p.GetEmailAddress(session)
109+ email, err := p.GetEmailAddress(context.Background(), session)
109110 assert.Equal(t, nil, err)
110111 assert.Equal(t, "michael.bland@gsa.gov", email)
111112}
@@ -118,7 +119,7 @@ func TestGitHubProviderGetEmailAddressNotVerified(t *testing.T) {
118119 p := testGitHubProvider(bURL.Host)
119120
120121 session := CreateAuthorizedSession()
121− email, err := p.GetEmailAddress(session)
122+ email, err := p.GetEmailAddress(context.Background(), session)
122123 assert.Equal(t, nil, err)
123124 assert.Empty(t, "", email)
124125}
@@ -136,7 +137,7 @@ func TestGitHubProviderGetEmailAddressWithOrg(t *testing.T) {
136137 p.Org = "testorg1"
137138
138139 session := CreateAuthorizedSession()
139− email, err := p.GetEmailAddress(session)
140+ email, err := p.GetEmailAddress(context.Background(), session)
140141 assert.Equal(t, nil, err)
141142 assert.Equal(t, "michael.bland@gsa.gov", email)
142143}
@@ -154,7 +155,7 @@ func TestGitHubProviderGetEmailAddressFailedRequest(t *testing.T) {
154155 // token. Alternatively, we could allow the parsing of the payload as
155156 // JSON to fail.
156157 session := &sessions.SessionState{AccessToken: "unexpected_access_token"}
157− email, err := p.GetEmailAddress(session)
158+ email, err := p.GetEmailAddress(context.Background(), session)
158159 assert.NotEqual(t, nil, err)
159160 assert.Equal(t, "", email)
160161}
@@ -167,7 +168,7 @@ func TestGitHubProviderGetEmailAddressEmailNotPresentInPayload(t *testing.T) {
167168 p := testGitHubProvider(bURL.Host)
168169
169170 session := CreateAuthorizedSession()
170− email, err := p.GetEmailAddress(session)
171+ email, err := p.GetEmailAddress(context.Background(), session)
171172 assert.NotEqual(t, nil, err)
172173 assert.Equal(t, "", email)
173174}
@@ -180,7 +181,7 @@ func TestGitHubProviderGetUserName(t *testing.T) {
180181 p := testGitHubProvider(bURL.Host)
181182
182183 session := CreateAuthorizedSession()
183− email, err := p.GetUserName(session)
184+ email, err := p.GetUserName(context.Background(), session)
184185 assert.Equal(t, nil, err)
185186 assert.Equal(t, "mbland", email)
186187}
providers/gitlab.go+13−14
@@ -25,6 +25,8 @@ type GitLabProvider struct {
2525 AllowUnverifiedEmail bool
2626}
2727
28+var _ Provider = (*GitLabProvider)(nil)
29+
2830// NewGitLabProvider initiates a new GitLabProvider
2931func NewGitLabProvider(p *ProviderData) *GitLabProvider {
3032 p.ProviderName = "GitLab"
@@ -37,13 +39,12 @@ func NewGitLabProvider(p *ProviderData) *GitLabProvider {
3739}
3840
3941// Redeem exchanges the OAuth2 authentication token for an ID token
40−func (p *GitLabProvider) Redeem(redirectURL, code string) (s *sessions.SessionState, err error) {
42+func (p *GitLabProvider) Redeem(ctx context.Context, redirectURL, code string) (s *sessions.SessionState, err error) {
4143 clientSecret, err := p.GetClientSecret()
4244 if err != nil {
4345 return
4446 }
4547
46− ctx := context.Background()
4748 c := oauth2.Config{
4849 ClientID: p.ClientID,
4950 ClientSecret: clientSecret,
@@ -65,14 +66,14 @@ func (p *GitLabProvider) Redeem(redirectURL, code string) (s *sessions.SessionSt
6566
6667// RefreshSessionIfNeeded checks if the session has expired and uses the
6768// RefreshToken to fetch a new ID token if required
68−func (p *GitLabProvider) RefreshSessionIfNeeded(s *sessions.SessionState) (bool, error) {
69+func (p *GitLabProvider) RefreshSessionIfNeeded(ctx context.Context, s *sessions.SessionState) (bool, error) {
6970 if s == nil || s.ExpiresOn.After(time.Now()) || s.RefreshToken == "" {
7071 return false, nil
7172 }
7273
7374 origExpiration := s.ExpiresOn
7475
75− err := p.redeemRefreshToken(s)
76+ err := p.redeemRefreshToken(ctx, s)
7677 if err != nil {
7778 return false, fmt.Errorf("unable to redeem refresh token: %v", err)
7879 }
@@ -81,7 +82,7 @@ func (p *GitLabProvider) RefreshSessionIfNeeded(s *sessions.SessionState) (bool,
8182 return true, nil
8283}
8384
84−func (p *GitLabProvider) redeemRefreshToken(s *sessions.SessionState) (err error) {
85+func (p *GitLabProvider) redeemRefreshToken(ctx context.Context, s *sessions.SessionState) (err error) {
8586 clientSecret, err := p.GetClientSecret()
8687 if err != nil {
8788 return
@@ -94,7 +95,6 @@ func (p *GitLabProvider) redeemRefreshToken(s *sessions.SessionState) (err error
9495 TokenURL: p.RedeemURL.String(),
9596 },
9697 }
97− ctx := context.Background()
9898 t := &oauth2.Token{
9999 RefreshToken: s.RefreshToken,
100100 Expiry: time.Now().Add(-time.Hour),
@@ -123,7 +123,7 @@ type gitlabUserInfo struct {
123123 Groups []string `json:"groups"`
124124}
125125
126−func (p *GitLabProvider) getUserInfo(s *sessions.SessionState) (*gitlabUserInfo, error) {
126+func (p *GitLabProvider) getUserInfo(ctx context.Context, s *sessions.SessionState) (*gitlabUserInfo, error) {
127127 // Retrieve user info JSON
128128 // https://docs.gitlab.com/ee/integration/openid_connect_provider.html#shared-information
129129
@@ -131,7 +131,7 @@ func (p *GitLabProvider) getUserInfo(s *sessions.SessionState) (*gitlabUserInfo,
131131 userInfoURL := *p.LoginURL
132132 userInfoURL.Path = "/oauth/userinfo"
133133
134− req, err := http.NewRequest("GET", userInfoURL.String(), nil)
134+ req, err := http.NewRequestWithContext(ctx, "GET", userInfoURL.String(), nil)
135135 if err != nil {
136136 return nil, fmt.Errorf("failed to create user info request: %v", err)
137137 }
@@ -219,16 +219,15 @@ func (p *GitLabProvider) createSessionState(ctx context.Context, token *oauth2.T
219219}
220220
221221// ValidateSessionState checks that the session's IDToken is still valid
222−func (p *GitLabProvider) ValidateSessionState(s *sessions.SessionState) bool {
223− ctx := context.Background()
222+func (p *GitLabProvider) ValidateSessionState(ctx context.Context, s *sessions.SessionState) bool {
224223 _, err := p.Verifier.Verify(ctx, s.IDToken)
225224 return err == nil
226225}
227226
228227// GetEmailAddress returns the Account email address
229−func (p *GitLabProvider) GetEmailAddress(s *sessions.SessionState) (string, error) {
228+func (p *GitLabProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
230229 // Retrieve user info
231− userInfo, err := p.getUserInfo(s)
230+ userInfo, err := p.getUserInfo(ctx, s)
232231 if err != nil {
233232 return "", fmt.Errorf("failed to retrieve user info: %v", err)
234233 }
@@ -254,8 +253,8 @@ func (p *GitLabProvider) GetEmailAddress(s *sessions.SessionState) (string, erro
254253}
255254
256255// GetUserName returns the Account user name
257−func (p *GitLabProvider) GetUserName(s *sessions.SessionState) (string, error) {
258− userInfo, err := p.getUserInfo(s)
256+func (p *GitLabProvider) GetUserName(ctx context.Context, s *sessions.SessionState) (string, error) {
257+ userInfo, err := p.getUserInfo(ctx, s)
259258 if err != nil {
260259 return "", fmt.Errorf("failed to retrieve user info: %v", err)
261260 }
providers/gitlab_test.go+9−8
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "net/http"
56 "net/http/httptest"
67 "net/url"
@@ -63,7 +64,7 @@ func TestGitLabProviderBadToken(t *testing.T) {
6364 p := testGitLabProvider(bURL.Host)
6465
6566 session := &sessions.SessionState{AccessToken: "unexpected_gitlab_access_token"}
66− _, err := p.GetEmailAddress(session)
67+ _, err := p.GetEmailAddress(context.Background(), session)
6768 assert.NotEqual(t, nil, err)
6869}
6970
@@ -75,7 +76,7 @@ func TestGitLabProviderUnverifiedEmailDenied(t *testing.T) {
7576 p := testGitLabProvider(bURL.Host)
7677
7778 session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
78− _, err := p.GetEmailAddress(session)
79+ _, err := p.GetEmailAddress(context.Background(), session)
7980 assert.NotEqual(t, nil, err)
8081}
8182
@@ -88,7 +89,7 @@ func TestGitLabProviderUnverifiedEmailAllowed(t *testing.T) {
8889 p.AllowUnverifiedEmail = true
8990
9091 session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
91− email, err := p.GetEmailAddress(session)
92+ email, err := p.GetEmailAddress(context.Background(), session)
9293 assert.Equal(t, nil, err)
9394 assert.Equal(t, "foo@bar.com", email)
9495}
@@ -102,7 +103,7 @@ func TestGitLabProviderUsername(t *testing.T) {
102103 p.AllowUnverifiedEmail = true
103104
104105 session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
105− username, err := p.GetUserName(session)
106+ username, err := p.GetUserName(context.Background(), session)
106107 assert.Equal(t, nil, err)
107108 assert.Equal(t, "FooBar", username)
108109}
@@ -117,7 +118,7 @@ func TestGitLabProviderGroupMembershipValid(t *testing.T) {
117118 p.Group = "foo"
118119
119120 session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
120− email, err := p.GetEmailAddress(session)
121+ email, err := p.GetEmailAddress(context.Background(), session)
121122 assert.Equal(t, nil, err)
122123 assert.Equal(t, "foo@bar.com", email)
123124}
@@ -132,7 +133,7 @@ func TestGitLabProviderGroupMembershipMissing(t *testing.T) {
132133 p.Group = "baz"
133134
134135 session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
135− _, err := p.GetEmailAddress(session)
136+ _, err := p.GetEmailAddress(context.Background(), session)
136137 assert.NotEqual(t, nil, err)
137138}
138139
@@ -146,7 +147,7 @@ func TestGitLabProviderEmailDomainValid(t *testing.T) {
146147 p.EmailDomains = []string{"bar.com"}
147148
148149 session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
149− email, err := p.GetEmailAddress(session)
150+ email, err := p.GetEmailAddress(context.Background(), session)
150151 assert.Equal(t, nil, err)
151152 assert.Equal(t, "foo@bar.com", email)
152153}
@@ -161,6 +162,6 @@ func TestGitLabProviderEmailDomainInvalid(t *testing.T) {
161162 p.EmailDomains = []string{"baz.com"}
162163
163164 session := &sessions.SessionState{AccessToken: "gitlab_access_token"}
164− _, err := p.GetEmailAddress(session)
165+ _, err := p.GetEmailAddress(context.Background(), session)
165166 assert.NotEqual(t, nil, err)
166167}
providers/google.go+8−6
@@ -31,6 +31,8 @@ type GoogleProvider struct {
3131 GroupValidator func(string) bool
3232}
3333
34+var _ Provider = (*GoogleProvider)(nil)
35+
3436type claims struct {
3537 Subject string `json:"sub"`
3638 Email string `json:"email"`
@@ -98,7 +100,7 @@ func claimsFromIDToken(idToken string) (*claims, error) {
98100}
99101
100102// Redeem exchanges the OAuth2 authentication token for an ID token
101−func (p *GoogleProvider) Redeem(redirectURL, code string) (s *sessions.SessionState, err error) {
103+func (p *GoogleProvider) Redeem(ctx context.Context, redirectURL, code string) (s *sessions.SessionState, err error) {
102104 if code == "" {
103105 err = errors.New("missing code")
104106 return
@@ -115,7 +117,7 @@ func (p *GoogleProvider) Redeem(redirectURL, code string) (s *sessions.SessionSt
115117 params.Add("code", code)
116118 params.Add("grant_type", "authorization_code")
117119 var req *http.Request
118− req, err = http.NewRequest("POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
120+ req, err = http.NewRequestWithContext(ctx, "POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
119121 if err != nil {
120122 return
121123 }
@@ -242,12 +244,12 @@ func (p *GoogleProvider) ValidateGroup(email string) bool {
242244
243245// RefreshSessionIfNeeded checks if the session has expired and uses the
244246// RefreshToken to fetch a new ID token if required
245−func (p *GoogleProvider) RefreshSessionIfNeeded(s *sessions.SessionState) (bool, error) {
247+func (p *GoogleProvider) RefreshSessionIfNeeded(ctx context.Context, s *sessions.SessionState) (bool, error) {
246248 if s == nil || s.ExpiresOn.After(time.Now()) || s.RefreshToken == "" {
247249 return false, nil
248250 }
249251
250− newToken, newIDToken, duration, err := p.redeemRefreshToken(s.RefreshToken)
252+ newToken, newIDToken, duration, err := p.redeemRefreshToken(ctx, s.RefreshToken)
251253 if err != nil {
252254 return false, err
253255 }
@@ -265,7 +267,7 @@ func (p *GoogleProvider) RefreshSessionIfNeeded(s *sessions.SessionState) (bool,
265267 return true, nil
266268}
267269
268−func (p *GoogleProvider) redeemRefreshToken(refreshToken string) (token string, idToken string, expires time.Duration, err error) {
270+func (p *GoogleProvider) redeemRefreshToken(ctx context.Context, refreshToken string) (token string, idToken string, expires time.Duration, err error) {
269271 // https://developers.google.com/identity/protocols/OAuth2WebServer#refresh
270272 clientSecret, err := p.GetClientSecret()
271273 if err != nil {
@@ -278,7 +280,7 @@ func (p *GoogleProvider) redeemRefreshToken(refreshToken string) (token string,
278280 params.Add("refresh_token", refreshToken)
279281 params.Add("grant_type", "refresh_token")
280282 var req *http.Request
281− req, err = http.NewRequest("POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
283+ req, err = http.NewRequestWithContext(ctx, "POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
282284 if err != nil {
283285 return
284286 }
providers/google_test.go+5−5
@@ -102,7 +102,7 @@ func TestGoogleProviderGetEmailAddress(t *testing.T) {
102102 p.RedeemURL, server = newRedeemServer(body)
103103 defer server.Close()
104104
105− session, err := p.Redeem("http://redirect/", "code1234")
105+ session, err := p.Redeem(context.Background(), "http://redirect/", "code1234")
106106 assert.Equal(t, nil, err)
107107 assert.NotEqual(t, session, nil)
108108 assert.Equal(t, "michael.bland@gsa.gov", session.Email)
@@ -139,7 +139,7 @@ func TestGoogleProviderGetEmailAddressInvalidEncoding(t *testing.T) {
139139 p.RedeemURL, server = newRedeemServer(body)
140140 defer server.Close()
141141
142− session, err := p.Redeem("http://redirect/", "code1234")
142+ session, err := p.Redeem(context.Background(), "http://redirect/", "code1234")
143143 assert.NotEqual(t, nil, err)
144144 if session != nil {
145145 t.Errorf("expect nill session %#v", session)
@@ -150,7 +150,7 @@ func TestGoogleProviderRedeemFailsNoCLientSecret(t *testing.T) {
150150 p := newGoogleProvider()
151151 p.ProviderData.ClientSecretFile = "srvnoerre"
152152
153− session, err := p.Redeem("http://redirect/", "code1234")
153+ session, err := p.Redeem(context.Background(), "http://redirect/", "code1234")
154154 assert.NotEqual(t, nil, err)
155155 if session != nil {
156156 t.Errorf("expect nill session %#v", session)
@@ -170,7 +170,7 @@ func TestGoogleProviderGetEmailAddressInvalidJson(t *testing.T) {
170170 p.RedeemURL, server = newRedeemServer(body)
171171 defer server.Close()
172172
173− session, err := p.Redeem("http://redirect/", "code1234")
173+ session, err := p.Redeem(context.Background(), "http://redirect/", "code1234")
174174 assert.NotEqual(t, nil, err)
175175 if session != nil {
176176 t.Errorf("expect nill session %#v", session)
@@ -189,7 +189,7 @@ func TestGoogleProviderGetEmailAddressEmailMissing(t *testing.T) {
189189 p.RedeemURL, server = newRedeemServer(body)
190190 defer server.Close()
191191
192− session, err := p.Redeem("http://redirect/", "code1234")
192+ session, err := p.Redeem(context.Background(), "http://redirect/", "code1234")
193193 assert.NotEqual(t, nil, err)
194194 if session != nil {
195195 t.Errorf("expect nill session %#v", session)
providers/internal_util.go+3−2
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "io/ioutil"
56 "net/http"
67 "net/url"
@@ -46,7 +47,7 @@ func stripParam(param, endpoint string) string {
4647}
4748
4849// validateToken returns true if token is valid
49−func validateToken(p Provider, accessToken string, header http.Header) bool {
50+func validateToken(ctx context.Context, p Provider, accessToken string, header http.Header) bool {
5051 if accessToken == "" || p.Data().ValidateURL == nil || p.Data().ValidateURL.String() == "" {
5152 return false
5253 }
@@ -55,7 +56,7 @@ func validateToken(p Provider, accessToken string, header http.Header) bool {
5556 params := url.Values{"access_token": {accessToken}}
5657 endpoint = endpoint + "?" + params.Encode()
5758 }
58− resp, err := requests.RequestUnparsedResponse(endpoint, header)
59+ resp, err := requests.RequestUnparsedResponse(ctx, endpoint, header)
5960 if err != nil {
6061 logger.Printf("GET %s", stripToken(endpoint))
6162 logger.Printf("token validation request failed: %s", err)
providers/internal_util_test.go+11−8
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "errors"
56 "net/http"
67 "net/http/httptest"
@@ -20,13 +21,15 @@ type ValidateSessionStateTestProvider struct {
2021 *ProviderData
2122}
2223
23−func (tp *ValidateSessionStateTestProvider) GetEmailAddress(s *sessions.SessionState) (string, error) {
24+var _ Provider = (*ValidateSessionStateTestProvider)(nil)
25+
26+func (tp *ValidateSessionStateTestProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
2427 return "", errors.New("not implemented")
2528}
2629
2730// Note that we're testing the internal validateToken() used to implement
2831// several Provider's ValidateSessionState() implementations
29−func (tp *ValidateSessionStateTestProvider) ValidateSessionState(s *sessions.SessionState) bool {
32+func (tp *ValidateSessionStateTestProvider) ValidateSessionState(ctx context.Context, s *sessions.SessionState) bool {
3033 return false
3134}
3235
@@ -87,7 +90,7 @@ func (vtTest *ValidateSessionStateTest) Close() {
8790func TestValidateSessionStateValidToken(t *testing.T) {
8891 vtTest := NewValidateSessionStateTest()
8992 defer vtTest.Close()
90− assert.Equal(t, true, validateToken(vtTest.provider, "foobar", nil))
93+ assert.Equal(t, true, validateToken(context.Background(), vtTest.provider, "foobar", nil))
9194}
9295
9396func TestValidateSessionStateValidTokenWithHeaders(t *testing.T) {
@@ -96,34 +99,34 @@ func TestValidateSessionStateValidTokenWithHeaders(t *testing.T) {
9699 vtTest.header = make(http.Header)
97100 vtTest.header.Set("Authorization", "Bearer foobar")
98101 assert.Equal(t, true,
99− validateToken(vtTest.provider, "foobar", vtTest.header))
102+ validateToken(context.Background(), vtTest.provider, "foobar", vtTest.header))
100103}
101104
102105func TestValidateSessionStateEmptyToken(t *testing.T) {
103106 vtTest := NewValidateSessionStateTest()
104107 defer vtTest.Close()
105− assert.Equal(t, false, validateToken(vtTest.provider, "", nil))
108+ assert.Equal(t, false, validateToken(context.Background(), vtTest.provider, "", nil))
106109}
107110
108111func TestValidateSessionStateEmptyValidateURL(t *testing.T) {
109112 vtTest := NewValidateSessionStateTest()
110113 defer vtTest.Close()
111114 vtTest.provider.Data().ValidateURL = nil
112− assert.Equal(t, false, validateToken(vtTest.provider, "foobar", nil))
115+ assert.Equal(t, false, validateToken(context.Background(), vtTest.provider, "foobar", nil))
113116}
114117
115118func TestValidateSessionStateRequestNetworkFailure(t *testing.T) {
116119 vtTest := NewValidateSessionStateTest()
117120 // Close immediately to simulate a network failure
118121 vtTest.Close()
119− assert.Equal(t, false, validateToken(vtTest.provider, "foobar", nil))
122+ assert.Equal(t, false, validateToken(context.Background(), vtTest.provider, "foobar", nil))
120123}
121124
122125func TestValidateSessionStateExpiredToken(t *testing.T) {
123126 vtTest := NewValidateSessionStateTest()
124127 defer vtTest.Close()
125128 vtTest.responseCode = 401
126− assert.Equal(t, false, validateToken(vtTest.provider, "foobar", nil))
129+ assert.Equal(t, false, validateToken(context.Background(), vtTest.provider, "foobar", nil))
127130}
128131
129132func TestStripTokenNotPresent(t *testing.T) {
providers/keycloak.go+5−2
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "net/http"
56 "net/url"
67
@@ -14,6 +15,8 @@ type KeycloakProvider struct {
1415 Group string
1516}
1617
18+var _ Provider = (*KeycloakProvider)(nil)
19+
1720func NewKeycloakProvider(p *ProviderData) *KeycloakProvider {
1821 p.ProviderName = "Keycloak"
1922 if p.LoginURL == nil || p.LoginURL.String() == "" {
@@ -47,9 +50,9 @@ func (p *KeycloakProvider) SetGroup(group string) {
4750 p.Group = group
4851}
4952
50−func (p *KeycloakProvider) GetEmailAddress(s *sessions.SessionState) (string, error) {
53+func (p *KeycloakProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
5154
52− req, err := http.NewRequest("GET", p.ValidateURL.String(), nil)
55+ req, err := http.NewRequestWithContext(ctx, "GET", p.ValidateURL.String(), nil)
5356 req.Header.Set("Authorization", "Bearer "+s.AccessToken)
5457 if err != nil {
5558 logger.Printf("failed building request %s", err)
providers/keycloak_test.go+5−4
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "net/http"
56 "net/http/httptest"
67 "net/url"
@@ -99,7 +100,7 @@ func TestKeycloakProviderGetEmailAddress(t *testing.T) {
99100 p := testKeycloakProvider(bURL.Host, "")
100101
101102 session := CreateAuthorizedSession()
102− email, err := p.GetEmailAddress(session)
103+ email, err := p.GetEmailAddress(context.Background(), session)
103104 assert.Equal(t, nil, err)
104105 assert.Equal(t, "michael.bland@gsa.gov", email)
105106}
@@ -112,7 +113,7 @@ func TestKeycloakProviderGetEmailAddressAndGroup(t *testing.T) {
112113 p := testKeycloakProvider(bURL.Host, "test-grp1")
113114
114115 session := CreateAuthorizedSession()
115− email, err := p.GetEmailAddress(session)
116+ email, err := p.GetEmailAddress(context.Background(), session)
116117 assert.Equal(t, nil, err)
117118 assert.Equal(t, "michael.bland@gsa.gov", email)
118119}
@@ -130,7 +131,7 @@ func TestKeycloakProviderGetEmailAddressFailedRequest(t *testing.T) {
130131 // token. Alternatively, we could allow the parsing of the payload as
131132 // JSON to fail.
132133 session := &sessions.SessionState{AccessToken: "unexpected_access_token"}
133− email, err := p.GetEmailAddress(session)
134+ email, err := p.GetEmailAddress(context.Background(), session)
134135 assert.NotEqual(t, nil, err)
135136 assert.Equal(t, "", email)
136137}
@@ -143,7 +144,7 @@ func TestKeycloakProviderGetEmailAddressEmailNotPresentInPayload(t *testing.T) {
143144 p := testKeycloakProvider(bURL.Host, "")
144145
145146 session := CreateAuthorizedSession()
146− email, err := p.GetEmailAddress(session)
147+ email, err := p.GetEmailAddress(context.Background(), session)
147148 assert.NotEqual(t, nil, err)
148149 assert.Equal(t, "", email)
149150}
providers/linkedin.go+7−4
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "errors"
56 "fmt"
67 "net/http"
@@ -15,6 +16,8 @@ type LinkedInProvider struct {
1516 *ProviderData
1617}
1718
19+var _ Provider = (*LinkedInProvider)(nil)
20+
1821// NewLinkedInProvider initiates a new LinkedInProvider
1922func NewLinkedInProvider(p *ProviderData) *LinkedInProvider {
2023 p.ProviderName = "LinkedIn"
@@ -51,11 +54,11 @@ func getLinkedInHeader(accessToken string) http.Header {
5154}
5255
5356// GetEmailAddress returns the Account email address
54−func (p *LinkedInProvider) GetEmailAddress(s *sessions.SessionState) (string, error) {
57+func (p *LinkedInProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
5558 if s.AccessToken == "" {
5659 return "", errors.New("missing access token")
5760 }
58− req, err := http.NewRequest("GET", p.ProfileURL.String()+"?format=json", nil)
61+ req, err := http.NewRequestWithContext(ctx, "GET", p.ProfileURL.String()+"?format=json", nil)
5962 if err != nil {
6063 return "", err
6164 }
@@ -74,6 +77,6 @@ func (p *LinkedInProvider) GetEmailAddress(s *sessions.SessionState) (string, er
7477}
7578
7679// ValidateSessionState validates the AccessToken
77−func (p *LinkedInProvider) ValidateSessionState(s *sessions.SessionState) bool {
78− return validateToken(p, s.AccessToken, getLinkedInHeader(s.AccessToken))
80+func (p *LinkedInProvider) ValidateSessionState(ctx context.Context, s *sessions.SessionState) bool {
81+ return validateToken(ctx, p, s.AccessToken, getLinkedInHeader(s.AccessToken))
7982}
providers/linkedin_test.go+4−3
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "net/http"
56 "net/http/httptest"
67 "net/url"
@@ -99,7 +100,7 @@ func TestLinkedInProviderGetEmailAddress(t *testing.T) {
99100 p := testLinkedInProvider(bURL.Host)
100101
101102 session := CreateAuthorizedSession()
102− email, err := p.GetEmailAddress(session)
103+ email, err := p.GetEmailAddress(context.Background(), session)
103104 assert.Equal(t, nil, err)
104105 assert.Equal(t, "user@linkedin.com", email)
105106}
@@ -115,7 +116,7 @@ func TestLinkedInProviderGetEmailAddressFailedRequest(t *testing.T) {
115116 // token. Alternatively, we could allow the parsing of the payload as
116117 // JSON to fail.
117118 session := &sessions.SessionState{AccessToken: "unexpected_access_token"}
118− email, err := p.GetEmailAddress(session)
119+ email, err := p.GetEmailAddress(context.Background(), session)
119120 assert.NotEqual(t, nil, err)
120121 assert.Equal(t, "", email)
121122}
@@ -128,7 +129,7 @@ func TestLinkedInProviderGetEmailAddressEmailNotPresentInPayload(t *testing.T) {
128129 p := testLinkedInProvider(bURL.Host)
129130
130131 session := CreateAuthorizedSession()
131− email, err := p.GetEmailAddress(session)
132+ email, err := p.GetEmailAddress(context.Background(), session)
132133 assert.NotEqual(t, nil, err)
133134 assert.Equal(t, "", email)
134135}
providers/logingov.go+8−5
@@ -2,6 +2,7 @@ package providers
22
33import (
44 "bytes"
5+ "context"
56 "crypto/rsa"
67 "encoding/json"
78 "errors"
@@ -28,6 +29,8 @@ type LoginGovProvider struct {
2829 PubJWKURL *url.URL
2930}
3031
32+var _ Provider = (*LoginGovProvider)(nil)
33+
3134// For generating a nonce
3235var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
3336
@@ -125,10 +128,10 @@ func checkNonce(idToken string, p *LoginGovProvider) (err error) {
125128 return
126129}
127130
128−func emailFromUserInfo(accessToken string, userInfoEndpoint string) (email string, err error) {
131+func emailFromUserInfo(ctx context.Context, accessToken string, userInfoEndpoint string) (email string, err error) {
129132 // query the user info endpoint for user attributes
130133 var req *http.Request
131− req, err = http.NewRequest("GET", userInfoEndpoint, nil)
134+ req, err = http.NewRequestWithContext(ctx, "GET", userInfoEndpoint, nil)
132135 if err != nil {
133136 return
134137 }
@@ -173,7 +176,7 @@ func emailFromUserInfo(accessToken string, userInfoEndpoint string) (email strin
173176}
174177
175178// Redeem exchanges the OAuth2 authentication token for an ID token
176−func (p *LoginGovProvider) Redeem(redirectURL, code string) (s *sessions.SessionState, err error) {
179+func (p *LoginGovProvider) Redeem(ctx context.Context, redirectURL, code string) (s *sessions.SessionState, err error) {
177180 if code == "" {
178181 err = errors.New("missing code")
179182 return
@@ -199,7 +202,7 @@ func (p *LoginGovProvider) Redeem(redirectURL, code string) (s *sessions.Session
199202 params.Add("grant_type", "authorization_code")
200203
201204 var req *http.Request
202− req, err = http.NewRequest("POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
205+ req, err = http.NewRequestWithContext(ctx, "POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
203206 if err != nil {
204207 return
205208 }
@@ -242,7 +245,7 @@ func (p *LoginGovProvider) Redeem(redirectURL, code string) (s *sessions.Session
242245
243246 // Get the email address
244247 var email string
245− email, err = emailFromUserInfo(jsonResponse.AccessToken, p.ProfileURL.String())
248+ email, err = emailFromUserInfo(ctx, jsonResponse.AccessToken, p.ProfileURL.String())
246249 if err != nil {
247250 return
248251 }
providers/logingov_test.go+3−2
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "crypto"
56 "crypto/rand"
67 "crypto/rsa"
@@ -189,7 +190,7 @@ func TestLoginGovProviderSessionData(t *testing.T) {
189190 p.PubJWKURL, pubjwkserver = newLoginGovServer(pubjwkbody)
190191 defer pubjwkserver.Close()
191192
192− session, err := p.Redeem("http://redirect/", "code1234")
193+ session, err := p.Redeem(context.Background(), "http://redirect/", "code1234")
193194 assert.NoError(t, err)
194195 assert.NotEqual(t, session, nil)
195196 assert.Equal(t, "timothy.spencer@gsa.gov", session.Email)
@@ -283,7 +284,7 @@ func TestLoginGovProviderBadNonce(t *testing.T) {
283284 p.PubJWKURL, pubjwkserver = newLoginGovServer(pubjwkbody)
284285 defer pubjwkserver.Close()
285286
286− _, err = p.Redeem("http://redirect/", "code1234")
287+ _, err = p.Redeem(context.Background(), "http://redirect/", "code1234")
287288
288289 // The "badfakenonce" in the idtoken above should cause this to error out
289290 assert.Error(t, err)
providers/nextcloud.go+5−2
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "fmt"
56 "net/http"
67
@@ -14,6 +15,8 @@ type NextcloudProvider struct {
1415 *ProviderData
1516}
1617
18+var _ Provider = (*NextcloudProvider)(nil)
19+
1720// NewNextcloudProvider initiates a new NextcloudProvider
1821func NewNextcloudProvider(p *ProviderData) *NextcloudProvider {
1922 p.ProviderName = "Nextcloud"
@@ -27,8 +30,8 @@ func getNextcloudHeader(accessToken string) http.Header {
2730}
2831
2932// GetEmailAddress returns the Account email address
30−func (p *NextcloudProvider) GetEmailAddress(s *sessions.SessionState) (string, error) {
31− req, err := http.NewRequest("GET",
33+func (p *NextcloudProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
34+ req, err := http.NewRequestWithContext(ctx, "GET",
3235 p.ValidateURL.String(), nil)
3336 if err != nil {
3437 logger.Printf("failed building request %s", err)
providers/nextcloud_test.go+4−3
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "net/http"
56 "net/http/httptest"
67 "net/url"
@@ -97,7 +98,7 @@ func TestNextcloudProviderGetEmailAddress(t *testing.T) {
9798 p.ValidateURL.RawQuery = formatJSON
9899
99100 session := CreateAuthorizedSession()
100− email, err := p.GetEmailAddress(session)
101+ email, err := p.GetEmailAddress(context.Background(), session)
101102 assert.Equal(t, nil, err)
102103 assert.Equal(t, "michael.bland@gsa.gov", email)
103104}
@@ -117,7 +118,7 @@ func TestNextcloudProviderGetEmailAddressFailedRequest(t *testing.T) {
117118 // token. Alternatively, we could allow the parsing of the payload as
118119 // JSON to fail.
119120 session := &sessions.SessionState{AccessToken: "unexpected_access_token"}
120− email, err := p.GetEmailAddress(session)
121+ email, err := p.GetEmailAddress(context.Background(), session)
121122 assert.NotEqual(t, nil, err)
122123 assert.Equal(t, "", email)
123124}
@@ -132,7 +133,7 @@ func TestNextcloudProviderGetEmailAddressEmailNotPresentInPayload(t *testing.T)
132133 p.ValidateURL.RawQuery = formatJSON
133134
134135 session := CreateAuthorizedSession()
135− email, err := p.GetEmailAddress(session)
136+ email, err := p.GetEmailAddress(context.Background(), session)
136137 assert.NotEqual(t, nil, err)
137138 assert.Equal(t, "", email)
138139}
providers/oidc.go+17−18
@@ -31,14 +31,15 @@ func NewOIDCProvider(p *ProviderData) *OIDCProvider {
3131 return &OIDCProvider{ProviderData: p}
3232}
3333
34+var _ Provider = (*OIDCProvider)(nil)
35+
3436// Redeem exchanges the OAuth2 authentication token for an ID token
35−func (p *OIDCProvider) Redeem(redirectURL, code string) (s *sessions.SessionState, err error) {
37+func (p *OIDCProvider) Redeem(ctx context.Context, redirectURL, code string) (s *sessions.SessionState, err error) {
3638 clientSecret, err := p.GetClientSecret()
3739 if err != nil {
3840 return
3941 }
4042
41− ctx := context.Background()
4243 c := oauth2.Config{
4344 ClientID: p.ClientID,
4445 ClientSecret: clientSecret,
@@ -60,7 +61,7 @@ func (p *OIDCProvider) Redeem(redirectURL, code string) (s *sessions.SessionStat
6061 return nil, fmt.Errorf("token response did not contain an id_token")
6162 }
6263
63− s, err = p.createSessionState(token, idToken)
64+ s, err = p.createSessionState(ctx, token, idToken)
6465 if err != nil {
6566 return nil, fmt.Errorf("unable to update session: %v", err)
6667 }
@@ -70,12 +71,12 @@ func (p *OIDCProvider) Redeem(redirectURL, code string) (s *sessions.SessionStat
7071
7172// RefreshSessionIfNeeded checks if the session has expired and uses the
7273// RefreshToken to fetch a new Access Token (and optional ID token) if required
73−func (p *OIDCProvider) RefreshSessionIfNeeded(s *sessions.SessionState) (bool, error) {
74+func (p *OIDCProvider) RefreshSessionIfNeeded(ctx context.Context, s *sessions.SessionState) (bool, error) {
7475 if s == nil || s.ExpiresOn.After(time.Now()) || s.RefreshToken == "" {
7576 return false, nil
7677 }
7778
78− err := p.redeemRefreshToken(s)
79+ err := p.redeemRefreshToken(ctx, s)
7980 if err != nil {
8081 return false, fmt.Errorf("unable to redeem refresh token: %v", err)
8182 }
@@ -84,7 +85,7 @@ func (p *OIDCProvider) RefreshSessionIfNeeded(s *sessions.SessionState) (bool, e
8485 return true, nil
8586}
8687
87−func (p *OIDCProvider) redeemRefreshToken(s *sessions.SessionState) (err error) {
88+func (p *OIDCProvider) redeemRefreshToken(ctx context.Context, s *sessions.SessionState) (err error) {
8889 clientSecret, err := p.GetClientSecret()
8990 if err != nil {
9091 return
@@ -97,7 +98,6 @@ func (p *OIDCProvider) redeemRefreshToken(s *sessions.SessionState) (err error)
9798 TokenURL: p.RedeemURL.String(),
9899 },
99100 }
100− ctx := context.Background()
101101 t := &oauth2.Token{
102102 RefreshToken: s.RefreshToken,
103103 Expiry: time.Now().Add(-time.Hour),
@@ -113,7 +113,7 @@ func (p *OIDCProvider) redeemRefreshToken(s *sessions.SessionState) (err error)
113113 return fmt.Errorf("unable to extract id_token from response: %v", err)
114114 }
115115
116− newSession, err := p.createSessionState(token, idToken)
116+ newSession, err := p.createSessionState(ctx, token, idToken)
117117 if err != nil {
118118 return fmt.Errorf("unable create new session state from response: %v", err)
119119 }
@@ -149,7 +149,7 @@ func (p *OIDCProvider) findVerifiedIDToken(ctx context.Context, token *oauth2.To
149149 return nil, nil
150150}
151151
152−func (p *OIDCProvider) createSessionState(token *oauth2.Token, idToken *oidc.IDToken) (*sessions.SessionState, error) {
152+func (p *OIDCProvider) createSessionState(ctx context.Context, token *oauth2.Token, idToken *oidc.IDToken) (*sessions.SessionState, error) {
153153
154154 var newSession *sessions.SessionState
155155
@@ -157,7 +157,7 @@ func (p *OIDCProvider) createSessionState(token *oauth2.Token, idToken *oidc.IDT
157157 newSession = &sessions.SessionState{}
158158 } else {
159159 var err error
160− newSession, err = p.createSessionStateInternal(token.Extra("id_token").(string), idToken, token)
160+ newSession, err = p.createSessionStateInternal(ctx, token.Extra("id_token").(string), idToken, token)
161161 if err != nil {
162162 return nil, err
163163 }
@@ -170,8 +170,8 @@ func (p *OIDCProvider) createSessionState(token *oauth2.Token, idToken *oidc.IDT
170170 return newSession, nil
171171}
172172
173−func (p *OIDCProvider) CreateSessionStateFromBearerToken(rawIDToken string, idToken *oidc.IDToken) (*sessions.SessionState, error) {
174− newSession, err := p.createSessionStateInternal(rawIDToken, idToken, nil)
173+func (p *OIDCProvider) CreateSessionStateFromBearerToken(ctx context.Context, rawIDToken string, idToken *oidc.IDToken) (*sessions.SessionState, error) {
174+ newSession, err := p.createSessionStateInternal(ctx, rawIDToken, idToken, nil)
175175 if err != nil {
176176 return nil, err
177177 }
@@ -184,7 +184,7 @@ func (p *OIDCProvider) CreateSessionStateFromBearerToken(rawIDToken string, idTo
184184 return newSession, nil
185185}
186186
187−func (p *OIDCProvider) createSessionStateInternal(rawIDToken string, idToken *oidc.IDToken, token *oauth2.Token) (*sessions.SessionState, error) {
187+func (p *OIDCProvider) createSessionStateInternal(ctx context.Context, rawIDToken string, idToken *oidc.IDToken, token *oauth2.Token) (*sessions.SessionState, error) {
188188
189189 newSession := &sessions.SessionState{}
190190
@@ -196,7 +196,7 @@ func (p *OIDCProvider) createSessionStateInternal(rawIDToken string, idToken *oi
196196 accessToken = token.AccessToken
197197 }
198198
199− claims, err := p.findClaimsFromIDToken(idToken, accessToken, p.ProfileURL.String())
199+ claims, err := p.findClaimsFromIDToken(ctx, idToken, accessToken, p.ProfileURL.String())
200200 if err != nil {
201201 return nil, fmt.Errorf("couldn't extract claims from id_token (%e)", err)
202202 }
@@ -217,8 +217,7 @@ func (p *OIDCProvider) createSessionStateInternal(rawIDToken string, idToken *oi
217217}
218218
219219// ValidateSessionState checks that the session's IDToken is still valid
220−func (p *OIDCProvider) ValidateSessionState(s *sessions.SessionState) bool {
221− ctx := context.Background()
220+func (p *OIDCProvider) ValidateSessionState(ctx context.Context, s *sessions.SessionState) bool {
222221 _, err := p.Verifier.Verify(ctx, s.IDToken)
223222 return err == nil
224223}
@@ -230,7 +229,7 @@ func getOIDCHeader(accessToken string) http.Header {
230229 return header
231230}
232231
233−func (p *OIDCProvider) findClaimsFromIDToken(idToken *oidc.IDToken, accessToken string, profileURL string) (*OIDCClaims, error) {
232+func (p *OIDCProvider) findClaimsFromIDToken(ctx context.Context, idToken *oidc.IDToken, accessToken string, profileURL string) (*OIDCClaims, error) {
234233
235234 claims := &OIDCClaims{}
236235 // Extract default claims.
@@ -257,7 +256,7 @@ func (p *OIDCProvider) findClaimsFromIDToken(idToken *oidc.IDToken, accessToken
257256 // contents at the profileURL contains the email.
258257 // Make a query to the userinfo endpoint, and attempt to locate the email from there.
259258
260− req, err := http.NewRequest("GET", profileURL, nil)
259+ req, err := http.NewRequestWithContext(ctx, "GET", profileURL, nil)
261260 if err != nil {
262261 return nil, err
263262 }
providers/oidc_test.go+4−4
@@ -159,7 +159,7 @@ func TestOIDCProviderRedeem(t *testing.T) {
159159 server, provider := newTestSetup(body)
160160 defer server.Close()
161161
162− session, err := provider.Redeem(provider.RedeemURL.String(), "code1234")
162+ session, err := provider.Redeem(context.Background(), provider.RedeemURL.String(), "code1234")
163163 assert.Equal(t, nil, err)
164164 assert.Equal(t, defaultIDToken.Email, session.Email)
165165 assert.Equal(t, accessToken, session.AccessToken)
@@ -183,7 +183,7 @@ func TestOIDCProviderRedeem_custom_userid(t *testing.T) {
183183 provider.UserIDClaim = "phone_number"
184184 defer server.Close()
185185
186− session, err := provider.Redeem(provider.RedeemURL.String(), "code1234")
186+ session, err := provider.Redeem(context.Background(), provider.RedeemURL.String(), "code1234")
187187 assert.Equal(t, nil, err)
188188 assert.Equal(t, defaultIDToken.Phone, session.Email)
189189}
@@ -211,7 +211,7 @@ func TestOIDCProviderRefreshSessionIfNeededWithoutIdToken(t *testing.T) {
211211 User: "11223344",
212212 }
213213
214− refreshed, err := provider.RefreshSessionIfNeeded(existingSession)
214+ refreshed, err := provider.RefreshSessionIfNeeded(context.Background(), existingSession)
215215 assert.Equal(t, nil, err)
216216 assert.Equal(t, refreshed, true)
217217 assert.Equal(t, "janedoe@example.com", existingSession.Email)
@@ -244,7 +244,7 @@ func TestOIDCProviderRefreshSessionIfNeededWithIdToken(t *testing.T) {
244244 Email: "changeit",
245245 User: "changeit",
246246 }
247− refreshed, err := provider.RefreshSessionIfNeeded(existingSession)
247+ refreshed, err := provider.RefreshSessionIfNeeded(context.Background(), existingSession)
248248 assert.Equal(t, nil, err)
249249 assert.Equal(t, refreshed, true)
250250 assert.Equal(t, defaultIDToken.Email, existingSession.Email)
providers/provider_default.go+12−9
@@ -2,6 +2,7 @@ package providers
22
33import (
44 "bytes"
5+ "context"
56 "encoding/json"
67 "errors"
78 "fmt"
@@ -16,8 +17,10 @@ import (
1617 "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption"
1718)
1819
20+var _ Provider = (*ProviderData)(nil)
21+
1922// Redeem provides a default implementation of the OAuth2 token redemption process
20−func (p *ProviderData) Redeem(redirectURL, code string) (s *sessions.SessionState, err error) {
23+func (p *ProviderData) Redeem(ctx context.Context, redirectURL, code string) (s *sessions.SessionState, err error) {
2124 if code == "" {
2225 err = errors.New("missing code")
2326 return
@@ -38,7 +41,7 @@ func (p *ProviderData) Redeem(redirectURL, code string) (s *sessions.SessionStat
3841 }
3942
4043 var req *http.Request
41− req, err = http.NewRequest("POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
44+ req, err = http.NewRequestWithContext(ctx, "POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
4245 if err != nil {
4346 return
4447 }
@@ -116,17 +119,17 @@ func (p *ProviderData) SessionFromCookie(v string, c *encryption.Cipher) (s *ses
116119}
117120
118121// GetEmailAddress returns the Account email address
119−func (p *ProviderData) GetEmailAddress(s *sessions.SessionState) (string, error) {
122+func (p *ProviderData) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
120123 return "", errors.New("not implemented")
121124}
122125
123126// GetUserName returns the Account username
124−func (p *ProviderData) GetUserName(s *sessions.SessionState) (string, error) {
127+func (p *ProviderData) GetUserName(ctx context.Context, s *sessions.SessionState) (string, error) {
125128 return "", errors.New("not implemented")
126129}
127130
128131// GetPreferredUsername returns the Account preferred username
129−func (p *ProviderData) GetPreferredUsername(s *sessions.SessionState) (string, error) {
132+func (p *ProviderData) GetPreferredUsername(ctx context.Context, s *sessions.SessionState) (string, error) {
130133 return "", errors.New("not implemented")
131134}
132135
@@ -137,17 +140,17 @@ func (p *ProviderData) ValidateGroup(email string) bool {
137140}
138141
139142// ValidateSessionState validates the AccessToken
140−func (p *ProviderData) ValidateSessionState(s *sessions.SessionState) bool {
141− return validateToken(p, s.AccessToken, nil)
143+func (p *ProviderData) ValidateSessionState(ctx context.Context, s *sessions.SessionState) bool {
144+ return validateToken(ctx, p, s.AccessToken, nil)
142145}
143146
144147// RefreshSessionIfNeeded should refresh the user's session if required and
145148// do nothing if a refresh is not required
146−func (p *ProviderData) RefreshSessionIfNeeded(s *sessions.SessionState) (bool, error) {
149+func (p *ProviderData) RefreshSessionIfNeeded(ctx context.Context, s *sessions.SessionState) (bool, error) {
147150 return false, nil
148151}
149152
150−func (p *ProviderData) CreateSessionStateFromBearerToken(rawIDToken string, idToken *oidc.IDToken) (*sessions.SessionState, error) {
153+func (p *ProviderData) CreateSessionStateFromBearerToken(ctx context.Context, rawIDToken string, idToken *oidc.IDToken) (*sessions.SessionState, error) {
151154 var claims struct {
152155 Subject string `json:"sub"`
153156 Email string `json:"email"`
providers/provider_default_test.go+2−1
@@ -1,6 +1,7 @@
11package providers
22
33import (
4+ "context"
45 "testing"
56 "time"
67
@@ -10,7 +11,7 @@ import (
1011
1112func TestRefresh(t *testing.T) {
1213 p := &ProviderData{}
13− refreshed, err := p.RefreshSessionIfNeeded(&sessions.SessionState{
14+ refreshed, err := p.RefreshSessionIfNeeded(context.Background(), &sessions.SessionState{
1415 ExpiresOn: time.Now().Add(time.Duration(-11) * time.Minute),
1516 })
1617 assert.Equal(t, false, refreshed)
providers/providers.go+9−7
@@ -1,6 +1,8 @@
11package providers
22
33import (
4+ "context"
5+
46 "github.com/coreos/go-oidc"
57 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
68 "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption"
@@ -9,17 +11,17 @@ import (
911// Provider represents an upstream identity provider implementation
1012type Provider interface {
1113 Data() *ProviderData
12− GetEmailAddress(*sessions.SessionState) (string, error)
13− GetUserName(*sessions.SessionState) (string, error)
14− GetPreferredUsername(*sessions.SessionState) (string, error)
15− Redeem(string, string) (*sessions.SessionState, error)
14+ GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error)
15+ GetUserName(ctx context.Context, s *sessions.SessionState) (string, error)
16+ GetPreferredUsername(ctx context.Context, s *sessions.SessionState) (string, error)
17+ Redeem(ctx context.Context, redirectURI, code string) (*sessions.SessionState, error)
1618 ValidateGroup(string) bool
17− ValidateSessionState(*sessions.SessionState) bool
19+ ValidateSessionState(ctx context.Context, s *sessions.SessionState) bool
1820 GetLoginURL(redirectURI, finalRedirect string) string
19− RefreshSessionIfNeeded(*sessions.SessionState) (bool, error)
21+ RefreshSessionIfNeeded(ctx context.Context, s *sessions.SessionState) (bool, error)
2022 SessionFromCookie(string, *encryption.Cipher) (*sessions.SessionState, error)
2123 CookieForSession(*sessions.SessionState, *encryption.Cipher) (string, error)
22− CreateSessionStateFromBearerToken(rawIDToken string, idToken *oidc.IDToken) (*sessions.SessionState, error)
24+ CreateSessionStateFromBearerToken(ctx context.Context, rawIDToken string, idToken *oidc.IDToken) (*sessions.SessionState, error)
2325}
2426
2527// New provides a new Provider based on the configured provider string