18 "github.com/coreos/go-oidc/v3/oidc"
19 "github.com/mbland/hmacauth"
20 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
21 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
22 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/cookies"
23 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
24 internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
25 sessionscookie "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/cookie"
26 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/upstream"
27 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/validation"
28 "github.com/oauth2-proxy/oauth2-proxy/v7/providers"
29 "github.com/stretchr/testify/assert"
30 "github.com/stretchr/testify/require"
34 // The rawCookieSecret is 32 bytes and the base64CookieSecret is the base64
35 // encoded version of this.
36 rawCookieSecret = "secretthirtytwobytes+abcdefghijk"
37 base64CookieSecret = "c2VjcmV0dGhpcnR5dHdvYnl0ZXMrYWJjZGVmZ2hpams"
38 clientID = "3984n253984d7348dm8234yf982t"
39 clientSecret = "gv3498mfc9t23y23974dm2394dm9"
43 logger.SetFlags(logger.Lshortfile)
46func TestRobotsTxt(t *testing.T) {
47 opts := baseTestOptions()
48 err := validation.Validate(opts)
49 assert.NoError(t, err)
51 proxy, err := NewOAuthProxy(opts, func(string) bool { return true })
55 rw := httptest.NewRecorder()
56 req, _ := http.NewRequest("GET", "/robots.txt", nil)
57 proxy.ServeHTTP(rw, req)
58 assert.Equal(t, 200, rw.Code)
59 assert.Equal(t, "User-agent: *\nDisallow: /\n", rw.Body.String())
62type TestProvider struct {
63 *providers.ProviderData
66 GroupValidator func(string) bool
69var _ providers.Provider = (*TestProvider)(nil)
71func NewTestProvider(providerURL *url.URL, emailAddress string) *TestProvider {
73 ProviderData: &providers.ProviderData{
74 ProviderName: "Test Provider",
77 Host: providerURL.Host,
78 Path: "/oauth/authorize",
82 Host: providerURL.Host,
87 Host: providerURL.Host,
88 Path: "/api/v1/profile",
90 Scope: "profile.email",
92 EmailAddress: emailAddress,
93 GroupValidator: func(s string) bool {
99func (tp *TestProvider) GetEmailAddress(_ context.Context, _ *sessions.SessionState) (string, error) {
100 return tp.EmailAddress, nil
103func (tp *TestProvider) ValidateSession(_ context.Context, _ *sessions.SessionState) bool {
107func Test_redeemCode(t *testing.T) {
108 opts := baseTestOptions()
109 err := validation.Validate(opts)
110 assert.NoError(t, err)
112 proxy, err := NewOAuthProxy(opts, func(string) bool { return true })
117 req := httptest.NewRequest(http.MethodGet, "/", nil)
118 _, err = proxy.redeemCode(req)
119 assert.Equal(t, providers.ErrMissingCode, err)
122func Test_enrichSession(t *testing.T) {
124 sessionUser = "Mr Session"
125 sessionEmail = "session@example.com"
126 providerEmail = "provider@example.com"
129 testCases := map[string]struct {
130 session *sessions.SessionState
134 "Session already has enrichable fields": {
135 session: &sessions.SessionState{
139 expectedUser: sessionUser,
140 expectedEmail: sessionEmail,
142 "Session is missing Email and GetEmailAddress is implemented": {
143 session: &sessions.SessionState{
146 expectedUser: sessionUser,
147 expectedEmail: providerEmail,
149 "Session is missing User and GetUserName is not implemented": {
150 session: &sessions.SessionState{
154 expectedEmail: sessionEmail,
158 for name, tc := range testCases {
159 t.Run(name, func(t *testing.T) {
160 opts := baseTestOptions()
161 err := validation.Validate(opts)
162 assert.NoError(t, err)
164 proxy, err := NewOAuthProxy(opts, func(string) bool { return true })
168 proxy.provider = NewTestProvider(&url.URL{Host: "www.example.com"}, providerEmail)
170 err = proxy.enrichSessionState(context.Background(), tc.session)
171 assert.NoError(t, err)
172 assert.Equal(t, tc.expectedUser, tc.session.User)
173 assert.Equal(t, tc.expectedEmail, tc.session.Email)
178func TestBasicAuthPassword(t *testing.T) {
179 providerServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
180 logger.Printf("%#v", r)
184 payload = `{"access_token": "my_auth_token"}`
186 payload = r.Header.Get("Authorization")
188 payload = "No Authorization header found."
192 _, err := w.Write([]byte(payload))
198 basicAuthPassword := "This is a secure password"
199 opts := baseTestOptions()
200 opts.UpstreamServers = options.UpstreamConfig{
201 Upstreams: []options.Upstream{
203 ID: providerServer.URL,
205 URI: providerServer.URL,
210 opts.Cookie.Secure = false
211 opts.InjectRequestHeaders = []options.Header{
213 Name: "Authorization",
214 Values: []options.HeaderValue{
216 ClaimSource: &options.ClaimSource{
218 BasicAuthPassword: &options.SecretSource{
219 Value: []byte(basicAuthPassword),
227 err := validation.Validate(opts)
228 assert.NoError(t, err)
230 providerURL, _ := url.Parse(providerServer.URL)
231 const emailAddress = "john.doe@example.com"
233 proxy, err := NewOAuthProxy(opts, func(email string) bool {
234 return email == emailAddress
239 proxy.provider = NewTestProvider(providerURL, emailAddress)
241 // Save the required session
242 rw := httptest.NewRecorder()
243 req, _ := http.NewRequest("GET", "/", nil)
244 err = proxy.sessionStore.Save(rw, req, &sessions.SessionState{
247 assert.NoError(t, err)
249 // Extract the cookie value to inject into the test request
250 cookie := rw.Header().Values("Set-Cookie")[0]
252 req, _ = http.NewRequest("GET", "/", nil)
253 req.Header.Set("Cookie", cookie)
254 rw = httptest.NewRecorder()
255 proxy.ServeHTTP(rw, req)
257 // The username in the basic auth credentials is expected to be equal to the email address from the
258 // auth response, so we use the same variable here.
259 expectedHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(emailAddress+":"+basicAuthPassword))
260 assert.Equal(t, expectedHeader, rw.Body.String())
261 providerServer.Close()
264func TestPassGroupsHeadersWithGroups(t *testing.T) {
265 opts := baseTestOptions()
266 opts.InjectRequestHeaders = []options.Header{
268 Name: "X-Forwarded-Groups",
269 Values: []options.HeaderValue{
271 ClaimSource: &options.ClaimSource{
279 err := validation.Validate(opts)
280 assert.NoError(t, err)
282 const emailAddress = "john.doe@example.com"
283 const userName = "9fcab5c9b889a557"
285 groups := []string{"a", "b"}
286 created := time.Now()
287 session := &sessions.SessionState{
291 AccessToken: "oauth_token",
295 proxy, err := NewOAuthProxy(opts, func(email string) bool {
296 return email == emailAddress
298 assert.NoError(t, err)
300 // Save the required session
301 rw := httptest.NewRecorder()
302 req, _ := http.NewRequest("GET", "/", nil)
303 err = proxy.sessionStore.Save(rw, req, session)
304 assert.NoError(t, err)
306 // Extract the cookie value to inject into the test request
307 cookie := rw.Header().Values("Set-Cookie")[0]
309 req, _ = http.NewRequest("GET", "/", nil)
310 req.Header.Set("Cookie", cookie)
311 rw = httptest.NewRecorder()
312 proxy.ServeHTTP(rw, req)
314 assert.Equal(t, []string{"a,b"}, req.Header["X-Forwarded-Groups"])
317type PassAccessTokenTest struct {
318 providerServer *httptest.Server
320 opts *options.Options
323type PassAccessTokenTestOptions struct {
326 ProxyUpstream options.Upstream
329func NewPassAccessTokenTest(opts PassAccessTokenTestOptions) (*PassAccessTokenTest, error) {
330 patt := &PassAccessTokenTest{}
332 patt.providerServer = httptest.NewServer(
333 http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
337 payload = `{"access_token": "my_auth_token"}`
339 payload = r.Header.Get("X-Forwarded-Access-Token")
341 payload = "No access token found."
345 _, err := w.Write([]byte(payload))
351 patt.opts = baseTestOptions()
352 patt.opts.UpstreamServers = options.UpstreamConfig{
353 Upstreams: []options.Upstream{
355 ID: patt.providerServer.URL,
357 URI: patt.providerServer.URL,
361 if opts.ProxyUpstream.ID != "" {
362 patt.opts.UpstreamServers.Upstreams = append(patt.opts.UpstreamServers.Upstreams, opts.ProxyUpstream)
365 patt.opts.Cookie.Secure = false
366 if opts.PassAccessToken {
367 patt.opts.InjectRequestHeaders = []options.Header{
369 Name: "X-Forwarded-Access-Token",
370 Values: []options.HeaderValue{
372 ClaimSource: &options.ClaimSource{
373 Claim: "access_token",
381 err := validation.Validate(patt.opts)
386 providerURL, _ := url.Parse(patt.providerServer.URL)
387 const emailAddress = "michael.bland@gsa.gov"
389 testProvider := NewTestProvider(providerURL, emailAddress)
390 testProvider.ValidToken = opts.ValidToken
391 patt.proxy, err = NewOAuthProxy(patt.opts, func(email string) bool {
392 return email == emailAddress
394 patt.proxy.provider = testProvider
401func (patTest *PassAccessTokenTest) Close() {
402 patTest.providerServer.Close()
405func (patTest *PassAccessTokenTest) getCallbackEndpoint() (httpCode int, cookie string) {
406 rw := httptest.NewRecorder()
408 csrf, err := cookies.NewCSRF(patTest.proxy.CookieOptions)
413 req, err := http.NewRequest(
416 "/oauth2/callback?code=callback_code&state=%s",
417 encodeState(csrf.HashOAuthState(), "%2F"),
419 strings.NewReader(""),
425 // rw is a dummy here, we just want the csrfCookie to add to our req
426 csrfCookie, err := csrf.SetCookie(httptest.NewRecorder(), req)
430 req.AddCookie(csrfCookie)
432 patTest.proxy.ServeHTTP(rw, req)
434 if len(rw.Header().Values("Set-Cookie")) >= 2 {
435 cookie = rw.Header().Values("Set-Cookie")[1]
438 return rw.Code, cookie
441// getEndpointWithCookie makes a requests againt the oauthproxy with passed requestPath
442// and cookie and returns body and status code.
443func (patTest *PassAccessTokenTest) getEndpointWithCookie(cookie string, endpoint string) (httpCode int, accessToken string) {
444 cookieName := patTest.proxy.CookieOptions.Name
446 keyPrefix := cookieName + "="
448 for _, field := range strings.Split(cookie, "; ") {
449 value = strings.TrimPrefix(field, keyPrefix)
460 req, err := http.NewRequest("GET", endpoint, strings.NewReader(""))
464 req.AddCookie(&http.Cookie{
468 Expires: time.Now().Add(time.Duration(24)),
472 rw := httptest.NewRecorder()
473 patTest.proxy.ServeHTTP(rw, req)
474 return rw.Code, rw.Body.String()
477func TestForwardAccessTokenUpstream(t *testing.T) {
478 patTest, err := NewPassAccessTokenTest(PassAccessTokenTestOptions{
479 PassAccessToken: true,
485 t.Cleanup(patTest.Close)
487 // A successful validation will redirect and set the auth cookie.
488 code, cookie := patTest.getCallbackEndpoint()
490 t.Fatalf("expected 302; got %d", code)
492 assert.NotNil(t, cookie)
494 // Now we make a regular request; the access_token from the cookie is
495 // forwarded as the "X-Forwarded-Access-Token" header. The token is
496 // read by the test provider server and written in the response body.
497 code, payload := patTest.getEndpointWithCookie(cookie, "/")
499 t.Fatalf("expected 200; got %d", code)
501 assert.Equal(t, "my_auth_token", payload)
504func TestStaticProxyUpstream(t *testing.T) {
505 patTest, err := NewPassAccessTokenTest(PassAccessTokenTestOptions{
506 PassAccessToken: true,
508 ProxyUpstream: options.Upstream{
510 Path: "/static-proxy",
517 t.Cleanup(patTest.Close)
519 // A successful validation will redirect and set the auth cookie.
520 code, cookie := patTest.getCallbackEndpoint()
522 t.Fatalf("expected 302; got %d", code)
524 assert.NotEqual(t, nil, cookie)
526 // Now we make a regular request against the upstream proxy; And validate
527 // the returned status code through the static proxy.
528 code, payload := patTest.getEndpointWithCookie(cookie, "/static-proxy")
530 t.Fatalf("expected 200; got %d", code)
532 assert.Equal(t, "Authenticated", payload)
535func TestDoNotForwardAccessTokenUpstream(t *testing.T) {
536 patTest, err := NewPassAccessTokenTest(PassAccessTokenTestOptions{
537 PassAccessToken: false,
543 t.Cleanup(patTest.Close)
545 // A successful validation will redirect and set the auth cookie.
546 code, cookie := patTest.getCallbackEndpoint()
548 t.Fatalf("expected 302; got %d", code)
550 assert.NotEqual(t, nil, cookie)
552 // Now we make a regular request, but the access token header should
554 code, payload := patTest.getEndpointWithCookie(cookie, "/")
556 t.Fatalf("expected 200; got %d", code)
558 assert.Equal(t, "No access token found.", payload)
561func TestSessionValidationFailure(t *testing.T) {
562 patTest, err := NewPassAccessTokenTest(PassAccessTokenTestOptions{
565 require.NoError(t, err)
566 t.Cleanup(patTest.Close)
568 // An unsuccessful validation will return 403 and not set the auth cookie.
569 code, cookie := patTest.getCallbackEndpoint()
570 assert.Equal(t, http.StatusForbidden, code)
571 assert.Equal(t, "", cookie)
574type SignInPageTest struct {
575 opts *options.Options
577 signInRegexp *regexp.Regexp
578 signInProviderRegexp *regexp.Regexp
581const signInRedirectPattern = `<input type="hidden" name="rd" value="(.*)">`
582const signInSkipProvider = `>Found<`
584func NewSignInPageTest(skipProvider bool) (*SignInPageTest, error) {
585 var sipTest SignInPageTest
587 sipTest.opts = baseTestOptions()
588 sipTest.opts.SkipProviderButton = skipProvider
589 err := validation.Validate(sipTest.opts)
594 sipTest.proxy, err = NewOAuthProxy(sipTest.opts, func(email string) bool {
600 sipTest.signInRegexp = regexp.MustCompile(signInRedirectPattern)
601 sipTest.signInProviderRegexp = regexp.MustCompile(signInSkipProvider)
606func (sipTest *SignInPageTest) GetEndpoint(endpoint string) (int, string) {
607 rw := httptest.NewRecorder()
608 req, _ := http.NewRequest("GET", endpoint, strings.NewReader(""))
609 sipTest.proxy.ServeHTTP(rw, req)
610 return rw.Code, rw.Body.String()
613type AlwaysSuccessfulValidator struct {
616func (AlwaysSuccessfulValidator) Validate(user, password string) bool {
620func TestManualSignInStoresUserGroupsInTheSession(t *testing.T) {
621 userGroups := []string{"somegroup", "someothergroup"}
623 opts := baseTestOptions()
624 opts.HtpasswdUserGroups = userGroups
625 err := validation.Validate(opts)
630 proxy, err := NewOAuthProxy(opts, func(email string) bool {
636 proxy.basicAuthValidator = AlwaysSuccessfulValidator{}
638 rw := httptest.NewRecorder()
639 formData := url.Values{}
640 formData.Set("username", "someuser")
641 formData.Set("password", "somepass")
642 signInReq, _ := http.NewRequest(http.MethodPost, "/oauth2/sign_in", strings.NewReader(formData.Encode()))
643 signInReq.Header.Add("Content-Type", "application/x-www-form-urlencoded")
644 proxy.ServeHTTP(rw, signInReq)
646 assert.Equal(t, http.StatusFound, rw.Code)
648 req, _ := http.NewRequest(http.MethodGet, "/something", strings.NewReader(formData.Encode()))
649 for _, c := range rw.Result().Cookies() {
653 s, err := proxy.sessionStore.Load(req)
657 assert.Equal(t, userGroups, s.Groups)
660func TestSignInPageIncludesTargetRedirect(t *testing.T) {
661 sipTest, err := NewSignInPageTest(false)
665 const endpoint = "/some/random/endpoint"
667 code, body := sipTest.GetEndpoint(endpoint)
668 assert.Equal(t, 403, code)
670 match := sipTest.signInRegexp.FindStringSubmatch(body)
672 t.Fatal("Did not find pattern in body: " +
673 signInRedirectPattern + "\nBody:\n" + body)
675 if match[1] != endpoint {
676 t.Fatal(`expected redirect to "` + endpoint +
677 `", but was "` + match[1] + `"`)
681func TestSignInPageDirectAccessRedirectsToRoot(t *testing.T) {
682 sipTest, err := NewSignInPageTest(false)
686 code, body := sipTest.GetEndpoint("/oauth2/sign_in")
687 assert.Equal(t, 200, code)
689 match := sipTest.signInRegexp.FindStringSubmatch(body)
691 t.Fatal("Did not find pattern in body: " +
692 signInRedirectPattern + "\nBody:\n" + body)
695 t.Fatal(`expected redirect to "/", but was "` + match[1] + `"`)
699func TestSignInPageSkipProvider(t *testing.T) {
700 sipTest, err := NewSignInPageTest(true)
705 endpoint := "/some/random/endpoint"
707 code, body := sipTest.GetEndpoint(endpoint)
708 assert.Equal(t, 302, code)
710 match := sipTest.signInProviderRegexp.FindStringSubmatch(body)
712 t.Fatal("Did not find pattern in body: " +
713 signInSkipProvider + "\nBody:\n" + body)
717func TestSignInPageSkipProviderDirect(t *testing.T) {
718 sipTest, err := NewSignInPageTest(true)
723 endpoint := "/sign_in"
725 code, body := sipTest.GetEndpoint(endpoint)
726 assert.Equal(t, 302, code)
728 match := sipTest.signInProviderRegexp.FindStringSubmatch(body)
730 t.Fatal("Did not find pattern in body: " +
731 signInSkipProvider + "\nBody:\n" + body)
735type ProcessCookieTest struct {
736 opts *options.Options
738 rw *httptest.ResponseRecorder
743type ProcessCookieTestOpts struct {
744 providerValidateCookieResponse bool
747type OptionsModifier func(*options.Options)
749func NewProcessCookieTest(opts ProcessCookieTestOpts, modifiers ...OptionsModifier) (*ProcessCookieTest, error) {
750 var pcTest ProcessCookieTest
752 pcTest.opts = baseTestOptions()
753 for _, modifier := range modifiers {
754 modifier(pcTest.opts)
756 // First, set the CookieRefresh option so proxy.AesCipher is created,
757 // needed to encrypt the access_token.
758 pcTest.opts.Cookie.Refresh = time.Hour
759 err := validation.Validate(pcTest.opts)
764 pcTest.proxy, err = NewOAuthProxy(pcTest.opts, func(email string) bool {
765 return pcTest.validateUser
770 testProvider := &TestProvider{
771 ProviderData: &providers.ProviderData{},
772 ValidToken: opts.providerValidateCookieResponse,
775 groups := pcTest.opts.Providers[0].AllowedGroups
776 testProvider.AllowedGroups = make(map[string]struct{}, len(groups))
777 for _, group := range groups {
778 testProvider.AllowedGroups[group] = struct{}{}
780 pcTest.proxy.provider = testProvider
782 // Now, zero-out proxy.CookieRefresh for the cases that don't involve
783 // access_token validation.
784 pcTest.proxy.CookieOptions.Refresh = time.Duration(0)
785 pcTest.rw = httptest.NewRecorder()
786 pcTest.req, _ = http.NewRequest("GET", "/", strings.NewReader(""))
787 pcTest.validateUser = true
791func NewProcessCookieTestWithDefaults() (*ProcessCookieTest, error) {
792 return NewProcessCookieTest(ProcessCookieTestOpts{
793 providerValidateCookieResponse: true,
797func NewProcessCookieTestWithOptionsModifiers(modifiers ...OptionsModifier) (*ProcessCookieTest, error) {
798 return NewProcessCookieTest(ProcessCookieTestOpts{
799 providerValidateCookieResponse: true,
803func (p *ProcessCookieTest) SaveSession(s *sessions.SessionState) error {
804 err := p.proxy.SaveSession(p.rw, p.req, s)
808 for _, cookie := range p.rw.Result().Cookies() {
809 p.req.AddCookie(cookie)
814func (p *ProcessCookieTest) LoadCookiedSession() (*sessions.SessionState, error) {
815 return p.proxy.LoadCookiedSession(p.req)
818func TestLoadCookiedSession(t *testing.T) {
819 pcTest, err := NewProcessCookieTestWithDefaults()
824 created := time.Now()
825 startSession := &sessions.SessionState{Email: "john.doe@example.com", AccessToken: "my_access_token", CreatedAt: &created}
826 err = pcTest.SaveSession(startSession)
827 assert.NoError(t, err)
829 session, err := pcTest.LoadCookiedSession()
833 assert.Equal(t, startSession.Email, session.Email)
834 assert.Equal(t, "", session.User)
835 assert.Equal(t, startSession.AccessToken, session.AccessToken)
838func TestProcessCookieNoCookieError(t *testing.T) {
839 pcTest, err := NewProcessCookieTestWithDefaults()
844 session, err := pcTest.LoadCookiedSession()
845 assert.Error(t, err, "cookie \"_oauth2_proxy\" not present")
847 t.Errorf("expected nil session. got %#v", session)
851func TestProcessCookieRefreshNotSet(t *testing.T) {
852 pcTest, err := NewProcessCookieTestWithOptionsModifiers(func(opts *options.Options) {
853 opts.Cookie.Expire = time.Duration(23) * time.Hour
859 reference := time.Now().Add(time.Duration(-2) * time.Hour)
861 startSession := &sessions.SessionState{Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: &reference}
862 err = pcTest.SaveSession(startSession)
863 assert.NoError(t, err)
865 session, err := pcTest.LoadCookiedSession()
866 assert.Equal(t, nil, err)
867 if session.Age() < time.Duration(-2)*time.Hour {
868 t.Errorf("cookie too young %v", session.Age())
870 assert.Equal(t, startSession.Email, session.Email)
873func TestProcessCookieFailIfCookieExpired(t *testing.T) {
874 pcTest, err := NewProcessCookieTestWithOptionsModifiers(func(opts *options.Options) {
875 opts.Cookie.Expire = time.Duration(24) * time.Hour
881 reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
882 startSession := &sessions.SessionState{Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: &reference}
883 err = pcTest.SaveSession(startSession)
884 assert.NoError(t, err)
886 session, err := pcTest.LoadCookiedSession()
887 assert.NotEqual(t, nil, err)
889 t.Errorf("expected nil session %#v", session)
893func TestProcessCookieFailIfRefreshSetAndCookieExpired(t *testing.T) {
894 pcTest, err := NewProcessCookieTestWithOptionsModifiers(func(opts *options.Options) {
895 opts.Cookie.Expire = time.Duration(24) * time.Hour
901 reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
902 startSession := &sessions.SessionState{Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: &reference}
903 err = pcTest.SaveSession(startSession)
904 assert.NoError(t, err)
906 pcTest.proxy.CookieOptions.Refresh = time.Hour
907 session, err := pcTest.LoadCookiedSession()
908 assert.NotEqual(t, nil, err)
910 t.Errorf("expected nil session %#v", session)
914func NewUserInfoEndpointTest() (*ProcessCookieTest, error) {
915 pcTest, err := NewProcessCookieTestWithDefaults()
919 pcTest.req, _ = http.NewRequest("GET",
920 pcTest.opts.ProxyPrefix+"/userinfo", nil)
924func TestUserInfoEndpointAccepted(t *testing.T) {
925 testCases := []struct {
927 session *sessions.SessionState
928 expectedResponse string
931 name: "Full session",
932 session: &sessions.SessionState{
934 Email: "john.doe@example.com",
935 Groups: []string{"example", "groups"},
936 AccessToken: "my_access_token",
938 expectedResponse: "{\"user\":\"john.doe\",\"email\":\"john.doe@example.com\",\"groups\":[\"example\",\"groups\"]}\n",
941 name: "Minimal session",
942 session: &sessions.SessionState{
944 Email: "john.doe@example.com",
945 Groups: []string{"example", "groups"},
947 expectedResponse: "{\"user\":\"john.doe\",\"email\":\"john.doe@example.com\",\"groups\":[\"example\",\"groups\"]}\n",
951 session: &sessions.SessionState{
953 Email: "john.doe@example.com",
954 AccessToken: "my_access_token",
956 expectedResponse: "{\"user\":\"john.doe\",\"email\":\"john.doe@example.com\"}\n",
959 name: "With Preferred Username",
960 session: &sessions.SessionState{
962 PreferredUsername: "john",
963 Email: "john.doe@example.com",
964 Groups: []string{"example", "groups"},
965 AccessToken: "my_access_token",
967 expectedResponse: "{\"user\":\"john.doe\",\"email\":\"john.doe@example.com\",\"groups\":[\"example\",\"groups\"],\"preferredUsername\":\"john\"}\n",
971 for _, tc := range testCases {
972 t.Run(tc.name, func(t *testing.T) {
973 test, err := NewUserInfoEndpointTest()
977 err = test.SaveSession(tc.session)
978 assert.NoError(t, err)
980 test.proxy.ServeHTTP(test.rw, test.req)
981 assert.Equal(t, http.StatusOK, test.rw.Code)
982 bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
983 assert.Equal(t, tc.expectedResponse, string(bodyBytes))
988func TestUserInfoEndpointUnauthorizedOnNoCookieSetError(t *testing.T) {
989 test, err := NewUserInfoEndpointTest()
994 test.proxy.ServeHTTP(test.rw, test.req)
995 assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
998func TestEncodedUrlsStayEncoded(t *testing.T) {
999 encodeTest, err := NewSignInPageTest(false)
1003 code, _ := encodeTest.GetEndpoint("/%2F/test1/%2F/test2")
1004 assert.Equal(t, 403, code)
1007func NewAuthOnlyEndpointTest(querystring string, modifiers ...OptionsModifier) (*ProcessCookieTest, error) {
1008 pcTest, err := NewProcessCookieTestWithOptionsModifiers(modifiers...)
1012 pcTest.req, _ = http.NewRequest(
1014 fmt.Sprintf("%s/auth%s", pcTest.opts.ProxyPrefix, querystring),
1019func TestAuthOnlyEndpointAccepted(t *testing.T) {
1020 test, err := NewAuthOnlyEndpointTest("")
1025 created := time.Now()
1026 startSession := &sessions.SessionState{
1027 Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: &created}
1028 err = test.SaveSession(startSession)
1029 assert.NoError(t, err)
1031 test.proxy.ServeHTTP(test.rw, test.req)
1032 assert.Equal(t, http.StatusAccepted, test.rw.Code)
1033 bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
1034 assert.Equal(t, "", string(bodyBytes))
1037func TestAuthOnlyEndpointUnauthorizedOnNoCookieSetError(t *testing.T) {
1038 test, err := NewAuthOnlyEndpointTest("")
1043 test.proxy.ServeHTTP(test.rw, test.req)
1044 assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
1045 bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
1046 assert.Equal(t, "Unauthorized\n", string(bodyBytes))
1049func TestAuthOnlyEndpointUnauthorizedOnExpiration(t *testing.T) {
1050 test, err := NewAuthOnlyEndpointTest("", func(opts *options.Options) {
1051 opts.Cookie.Expire = time.Duration(24) * time.Hour
1057 reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
1058 startSession := &sessions.SessionState{
1059 Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: &reference}
1060 err = test.SaveSession(startSession)
1061 assert.NoError(t, err)
1063 test.proxy.ServeHTTP(test.rw, test.req)
1064 assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
1065 bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
1066 assert.Equal(t, "Unauthorized\n", string(bodyBytes))
1069func TestAuthOnlyEndpointUnauthorizedOnEmailValidationFailure(t *testing.T) {
1070 test, err := NewAuthOnlyEndpointTest("")
1075 created := time.Now()
1076 startSession := &sessions.SessionState{
1077 Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: &created}
1078 err = test.SaveSession(startSession)
1079 assert.NoError(t, err)
1080 test.validateUser = false
1082 test.proxy.ServeHTTP(test.rw, test.req)
1083 assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
1084 bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
1085 assert.Equal(t, "Unauthorized\n", string(bodyBytes))
1088func TestAuthOnlyEndpointSetXAuthRequestHeaders(t *testing.T) {
1089 var pcTest ProcessCookieTest
1091 pcTest.opts = baseTestOptions()
1092 pcTest.opts.InjectResponseHeaders = []options.Header{
1094 Name: "X-Auth-Request-User",
1095 Values: []options.HeaderValue{
1097 ClaimSource: &options.ClaimSource{
1104 Name: "X-Auth-Request-Email",
1105 Values: []options.HeaderValue{
1107 ClaimSource: &options.ClaimSource{
1114 Name: "X-Auth-Request-Groups",
1115 Values: []options.HeaderValue{
1117 ClaimSource: &options.ClaimSource{
1124 Name: "X-Forwarded-Preferred-Username",
1125 Values: []options.HeaderValue{
1127 ClaimSource: &options.ClaimSource{
1128 Claim: "preferred_username",
1134 pcTest.opts.Providers[0].AllowedGroups = []string{"oauth_groups"}
1135 err := validation.Validate(pcTest.opts)
1136 assert.NoError(t, err)
1138 pcTest.proxy, err = NewOAuthProxy(pcTest.opts, func(email string) bool {
1139 return pcTest.validateUser
1144 pcTest.proxy.provider = &TestProvider{
1145 ProviderData: &providers.ProviderData{},
1149 pcTest.validateUser = true
1151 pcTest.rw = httptest.NewRecorder()
1152 pcTest.req, _ = http.NewRequest("GET",
1153 pcTest.opts.ProxyPrefix+"/auth", nil)
1155 created := time.Now()
1156 startSession := &sessions.SessionState{
1157 User: "oauth_user", Groups: []string{"oauth_groups"}, Email: "oauth_user@example.com", AccessToken: "oauth_token", CreatedAt: &created}
1158 err = pcTest.SaveSession(startSession)
1159 assert.NoError(t, err)
1161 pcTest.proxy.ServeHTTP(pcTest.rw, pcTest.req)
1162 assert.Equal(t, http.StatusAccepted, pcTest.rw.Code)
1163 assert.Equal(t, "oauth_user", pcTest.rw.Header().Get("X-Auth-Request-User"))
1164 assert.Equal(t, startSession.Groups, pcTest.rw.Header().Values("X-Auth-Request-Groups"))
1165 assert.Equal(t, "oauth_user@example.com", pcTest.rw.Header().Get("X-Auth-Request-Email"))
1168func TestAuthOnlyEndpointSetBasicAuthTrueRequestHeaders(t *testing.T) {
1169 var pcTest ProcessCookieTest
1171 pcTest.opts = baseTestOptions()
1172 pcTest.opts.InjectResponseHeaders = []options.Header{
1174 Name: "X-Auth-Request-User",
1175 Values: []options.HeaderValue{
1177 ClaimSource: &options.ClaimSource{
1184 Name: "X-Auth-Request-Email",
1185 Values: []options.HeaderValue{
1187 ClaimSource: &options.ClaimSource{
1194 Name: "X-Auth-Request-Groups",
1195 Values: []options.HeaderValue{
1197 ClaimSource: &options.ClaimSource{
1204 Name: "X-Forwarded-Preferred-Username",
1205 Values: []options.HeaderValue{
1207 ClaimSource: &options.ClaimSource{
1208 Claim: "preferred_username",
1214 Name: "Authorization",
1215 Values: []options.HeaderValue{
1217 ClaimSource: &options.ClaimSource{
1219 BasicAuthPassword: &options.SecretSource{
1220 Value: []byte("This is a secure password"),
1228 err := validation.Validate(pcTest.opts)
1229 assert.NoError(t, err)
1231 pcTest.proxy, err = NewOAuthProxy(pcTest.opts, func(email string) bool {
1232 return pcTest.validateUser
1237 pcTest.proxy.provider = &TestProvider{
1238 ProviderData: &providers.ProviderData{},
1242 pcTest.validateUser = true
1244 pcTest.rw = httptest.NewRecorder()
1245 pcTest.req, _ = http.NewRequest("GET",
1246 pcTest.opts.ProxyPrefix+"/auth", nil)
1248 created := time.Now()
1249 startSession := &sessions.SessionState{
1250 User: "oauth_user", Email: "oauth_user@example.com", AccessToken: "oauth_token", CreatedAt: &created}
1251 err = pcTest.SaveSession(startSession)
1252 assert.NoError(t, err)
1254 pcTest.proxy.ServeHTTP(pcTest.rw, pcTest.req)
1255 assert.Equal(t, http.StatusAccepted, pcTest.rw.Code)
1256 assert.Equal(t, "oauth_user", pcTest.rw.Header().Values("X-Auth-Request-User")[0])
1257 assert.Equal(t, "oauth_user@example.com", pcTest.rw.Header().Values("X-Auth-Request-Email")[0])
1258 expectedHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte("oauth_user:This is a secure password"))
1259 assert.Equal(t, expectedHeader, pcTest.rw.Header().Values("Authorization")[0])
1262func TestAuthOnlyEndpointSetBasicAuthFalseRequestHeaders(t *testing.T) {
1263 var pcTest ProcessCookieTest
1265 pcTest.opts = baseTestOptions()
1266 pcTest.opts.InjectResponseHeaders = []options.Header{
1268 Name: "X-Auth-Request-User",
1269 Values: []options.HeaderValue{
1271 ClaimSource: &options.ClaimSource{
1278 Name: "X-Auth-Request-Email",
1279 Values: []options.HeaderValue{
1281 ClaimSource: &options.ClaimSource{
1288 Name: "X-Auth-Request-Groups",
1289 Values: []options.HeaderValue{
1291 ClaimSource: &options.ClaimSource{
1298 Name: "X-Forwarded-Preferred-Username",
1299 Values: []options.HeaderValue{
1301 ClaimSource: &options.ClaimSource{
1302 Claim: "preferred_username",
1308 err := validation.Validate(pcTest.opts)
1309 assert.NoError(t, err)
1311 pcTest.proxy, err = NewOAuthProxy(pcTest.opts, func(email string) bool {
1312 return pcTest.validateUser
1317 pcTest.proxy.provider = &TestProvider{
1318 ProviderData: &providers.ProviderData{},
1322 pcTest.validateUser = true
1324 pcTest.rw = httptest.NewRecorder()
1325 pcTest.req, _ = http.NewRequest("GET",
1326 pcTest.opts.ProxyPrefix+"/auth", nil)
1328 created := time.Now()
1329 startSession := &sessions.SessionState{
1330 User: "oauth_user", Email: "oauth_user@example.com", AccessToken: "oauth_token", CreatedAt: &created}
1331 err = pcTest.SaveSession(startSession)
1332 assert.NoError(t, err)
1334 pcTest.proxy.ServeHTTP(pcTest.rw, pcTest.req)
1335 assert.Equal(t, http.StatusAccepted, pcTest.rw.Code)
1336 assert.Equal(t, "oauth_user", pcTest.rw.Header().Values("X-Auth-Request-User")[0])
1337 assert.Equal(t, "oauth_user@example.com", pcTest.rw.Header().Values("X-Auth-Request-Email")[0])
1338 assert.Equal(t, 0, len(pcTest.rw.Header().Values("Authorization")), "should not have Authorization header entries")
1341func TestAuthSkippedForPreflightRequests(t *testing.T) {
1342 upstreamServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1344 _, err := w.Write([]byte("response"))
1349 t.Cleanup(upstreamServer.Close)
1351 opts := baseTestOptions()
1352 opts.UpstreamServers = options.UpstreamConfig{
1353 Upstreams: []options.Upstream{
1355 ID: upstreamServer.URL,
1357 URI: upstreamServer.URL,
1361 opts.SkipAuthPreflight = true
1362 err := validation.Validate(opts)
1363 assert.NoError(t, err)
1365 upstreamURL, _ := url.Parse(upstreamServer.URL)
1367 proxy, err := NewOAuthProxy(opts, func(string) bool { return false })
1371 proxy.provider = NewTestProvider(upstreamURL, "")
1372 rw := httptest.NewRecorder()
1373 req, _ := http.NewRequest("OPTIONS", "/preflight-request", nil)
1374 proxy.ServeHTTP(rw, req)
1376 assert.Equal(t, 200, rw.Code)
1377 assert.Equal(t, "response", rw.Body.String())
1380type SignatureAuthenticator struct {
1381 auth hmacauth.HmacAuth
1384func (v *SignatureAuthenticator) Authenticate(w http.ResponseWriter, r *http.Request) {
1385 result, headerSig, computedSig := v.auth.AuthenticateRequest(r)
1389 case hmacauth.ResultNoSignature:
1390 msg = "no signature received"
1391 case hmacauth.ResultMatch:
1392 msg = "signatures match"
1393 case hmacauth.ResultMismatch:
1395 "signatures do not match:\n received: %s\n computed: %s",
1399 panic("unknown result value: " + result.String())
1402 _, err := w.Write([]byte(msg))
1408type SignatureTest struct {
1409 opts *options.Options
1410 upstream *httptest.Server
1412 provider *httptest.Server
1414 rw *httptest.ResponseRecorder
1415 authenticator *SignatureAuthenticator
1416 authProvider providers.Provider
1419func NewSignatureTest() (*SignatureTest, error) {
1420 opts := baseTestOptions()
1421 opts.EmailDomains = []string{"acm.org"}
1423 authenticator := &SignatureAuthenticator{}
1424 upstreamServer := httptest.NewServer(
1425 http.HandlerFunc(authenticator.Authenticate))
1426 upstreamURL, err := url.Parse(upstreamServer.URL)
1430 opts.UpstreamServers = options.UpstreamConfig{
1431 Upstreams: []options.Upstream{
1433 ID: upstreamServer.URL,
1435 URI: upstreamServer.URL,
1440 providerHandler := func(w http.ResponseWriter, r *http.Request) {
1441 _, err := w.Write([]byte(`{"access_token": "my_auth_token"}`))
1446 provider := httptest.NewServer(http.HandlerFunc(providerHandler))
1447 providerURL, err := url.Parse(provider.URL)
1451 testProvider := NewTestProvider(providerURL, "mbland@acm.org")
1453 return &SignatureTest{
1459 httptest.NewRecorder(),
1465func (st *SignatureTest) Close() {
1470// fakeNetConn simulates an http.Request.Body buffer that will be consumed
1471// when it is read by the hmacauth.HmacAuth if not handled properly. See:
1472// https://github.com/18F/hmacauth/pull/4
1473type fakeNetConn struct {
1477func (fnc *fakeNetConn) Read(p []byte) (n int, err error) {
1478 if bodyLen := len(fnc.reqBody); bodyLen != 0 {
1479 copy(p, fnc.reqBody)
1481 return bodyLen, io.EOF
1486func (st *SignatureTest) MakeRequestWithExpectedKey(method, body, key string) error {
1487 err := validation.Validate(st.opts)
1491 proxy, err := NewOAuthProxy(st.opts, func(email string) bool { return true })
1495 proxy.provider = st.authProvider
1497 var bodyBuf io.ReadCloser
1499 bodyBuf = ioutil.NopCloser(&fakeNetConn{reqBody: body})
1501 req := httptest.NewRequest(method, "/foo/bar", bodyBuf)
1502 req.Header = st.header
1504 state := &sessions.SessionState{
1505 Email: "mbland@acm.org", AccessToken: "my_access_token"}
1506 err = proxy.SaveSession(st.rw, req, state)
1510 for _, c := range st.rw.Result().Cookies() {
1513 // This is used by the upstream to validate the signature.
1514 st.authenticator.auth = hmacauth.NewHmacAuth(
1515 crypto.SHA1, []byte(key), upstream.SignatureHeader, upstream.SignatureHeaders)
1516 proxy.ServeHTTP(st.rw, req)
1521func TestRequestSignature(t *testing.T) {
1522 testCases := map[string]struct {
1528 "No request signature": {
1532 resp: "no signature received",
1537 key: "7d9e1aa87a5954e6f9fc59266b3af9d7c35fda2d",
1538 resp: "signatures match",
1542 body: `{ "hello": "world!" }`,
1543 key: "d90df39e2d19282840252612dd7c81421a372f61",
1544 resp: "signatures match",
1547 for name, tc := range testCases {
1548 t.Run(name, func(t *testing.T) {
1549 st, err := NewSignatureTest()
1555 st.opts.SignatureKey = fmt.Sprintf("sha1:%s", tc.key)
1557 err = st.MakeRequestWithExpectedKey(tc.method, tc.body, tc.key)
1558 assert.NoError(t, err)
1559 assert.Equal(t, 200, st.rw.Code)
1560 assert.Equal(t, tc.resp, st.rw.Body.String())
1565type ajaxRequestTest struct {
1566 opts *options.Options
1570func newAjaxRequestTest(forceJSONErrors bool) (*ajaxRequestTest, error) {
1571 test := &ajaxRequestTest{}
1572 test.opts = baseTestOptions()
1573 test.opts.ForceJSONErrors = forceJSONErrors
1574 err := validation.Validate(test.opts)
1579 test.proxy, err = NewOAuthProxy(test.opts, func(email string) bool {
1588func (test *ajaxRequestTest) getEndpoint(endpoint string, header http.Header) (int, http.Header, []byte, error) {
1589 rw := httptest.NewRecorder()
1590 req, err := http.NewRequest(http.MethodGet, endpoint, strings.NewReader(""))
1592 return 0, nil, nil, err
1595 test.proxy.ServeHTTP(rw, req)
1596 return rw.Code, rw.Header(), rw.Body.Bytes(), nil
1599func testAjaxUnauthorizedRequest(t *testing.T, header http.Header, forceJSONErrors bool) {
1600 test, err := newAjaxRequestTest(forceJSONErrors)
1606 code, rh, body, err := test.getEndpoint(endpoint, header)
1607 assert.NoError(t, err)
1608 assert.Equal(t, http.StatusUnauthorized, code)
1609 mime := rh.Get("Content-Type")
1610 assert.Equal(t, applicationJSON, mime)
1611 assert.Equal(t, []byte("{}"), body)
1613func TestAjaxUnauthorizedRequest1(t *testing.T) {
1614 header := make(http.Header)
1615 header.Add("accept", applicationJSON)
1617 testAjaxUnauthorizedRequest(t, header, false)
1620func TestAjaxUnauthorizedRequest2(t *testing.T) {
1621 header := make(http.Header)
1622 header.Add("Accept", applicationJSON)
1624 testAjaxUnauthorizedRequest(t, header, false)
1627func TestAjaxUnauthorizedRequestAccept1(t *testing.T) {
1628 header := make(http.Header)
1629 header.Add("Accept", "application/json, text/plain, */*")
1631 testAjaxUnauthorizedRequest(t, header, false)
1634func TestForceJSONErrorsUnauthorizedRequest(t *testing.T) {
1635 testAjaxUnauthorizedRequest(t, nil, true)
1638func TestAjaxForbiddendRequest(t *testing.T) {
1639 test, err := newAjaxRequestTest(false)
1644 header := make(http.Header)
1645 code, rh, _, err := test.getEndpoint(endpoint, header)
1646 assert.NoError(t, err)
1647 assert.Equal(t, http.StatusForbidden, code)
1648 mime := rh.Get("Content-Type")
1649 assert.NotEqual(t, applicationJSON, mime)
1652func TestClearSplitCookie(t *testing.T) {
1653 opts := baseTestOptions()
1654 opts.Cookie.Secret = base64CookieSecret
1655 opts.Cookie.Name = "oauth2"
1656 opts.Cookie.Domains = []string{"abc"}
1657 err := validation.Validate(opts)
1658 assert.NoError(t, err)
1660 store, err := sessionscookie.NewCookieSessionStore(&opts.Session, &opts.Cookie)
1665 p := OAuthProxy{CookieOptions: &opts.Cookie, sessionStore: store}
1666 var rw = httptest.NewRecorder()
1667 req := httptest.NewRequest("get", "/", nil)
1669 req.AddCookie(&http.Cookie{
1673 req.AddCookie(&http.Cookie{
1677 req.AddCookie(&http.Cookie{
1682 err = p.ClearSessionCookie(rw, req)
1683 assert.NoError(t, err)
1684 header := rw.Header()
1686 assert.Equal(t, 2, len(header["Set-Cookie"]), "should have 3 set-cookie header entries")
1689func TestClearSingleCookie(t *testing.T) {
1690 opts := baseTestOptions()
1691 opts.Cookie.Name = "oauth2"
1692 opts.Cookie.Domains = []string{"abc"}
1693 store, err := sessionscookie.NewCookieSessionStore(&opts.Session, &opts.Cookie)
1698 p := OAuthProxy{CookieOptions: &opts.Cookie, sessionStore: store}
1699 var rw = httptest.NewRecorder()
1700 req := httptest.NewRequest("get", "/", nil)
1702 req.AddCookie(&http.Cookie{
1706 req.AddCookie(&http.Cookie{
1711 err = p.ClearSessionCookie(rw, req)
1712 assert.NoError(t, err)
1713 header := rw.Header()
1715 assert.Equal(t, 1, len(header["Set-Cookie"]), "should have 1 set-cookie header entries")
1718type NoOpKeySet struct {
1721func (NoOpKeySet) VerifySignature(_ context.Context, jwt string) (payload []byte, err error) {
1722 splitStrings := strings.Split(jwt, ".")
1723 payloadString := splitStrings[1]
1724 return base64.RawURLEncoding.DecodeString(payloadString)
1727func TestGetJwtSession(t *testing.T) {
1730 "sub": "1234567890",
1731 "aud": "https://test.myapp.com",
1733 "email": "john@example.com",
1734 "iss": "https://issuer.example.com",
1739 goodJwt := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9." +
1740 "eyJzdWIiOiIxMjM0NTY3ODkwIiwiYXVkIjoiaHR0cHM6Ly90ZXN0Lm15YXBwLmNvbSIsIm5hbWUiOiJKb2huIERvZSIsImVtY" +
1741 "WlsIjoiam9obkBleGFtcGxlLmNvbSIsImlzcyI6Imh0dHBzOi8vaXNzdWVyLmV4YW1wbGUuY29tIiwiaWF0IjoxNTUzNjkxMj" +
1742 "E1LCJleHAiOjE5MTIxNTE4MjF9." +
1743 "rLVyzOnEldUq_pNkfa-WiV8TVJYWyZCaM2Am_uo8FGg11zD7l-qmz3x1seTvqpH6Y0Ty00fmv6dJnGnC8WMnPXQiodRTfhBSe" +
1744 "OKZMu0HkMD2sg52zlKkbfLTO6ic5VnbVgwjjrB8am_Ta6w7kyFUaB5C1BsIrrLMldkWEhynbb8"
1746 keyset := NoOpKeySet{}
1747 verifier := oidc.NewVerifier("https://issuer.example.com", keyset,
1748 &oidc.Config{ClientID: "https://test.myapp.com", SkipExpiryCheck: true,
1749 SkipClientIDCheck: true})
1750 verificationOptions := internaloidc.IDTokenVerificationOptions{
1751 AudienceClaims: []string{"aud"},
1752 ClientID: "https://test.myapp.com",
1753 ExtraAudiences: []string{},
1755 internalVerifier := internaloidc.NewVerifier(verifier, verificationOptions)
1757 test, err := NewAuthOnlyEndpointTest("", func(opts *options.Options) {
1758 opts.InjectRequestHeaders = []options.Header{
1760 Name: "Authorization",
1761 Values: []options.HeaderValue{
1763 ClaimSource: &options.ClaimSource{
1771 Name: "X-Forwarded-User",
1772 Values: []options.HeaderValue{
1774 ClaimSource: &options.ClaimSource{
1781 Name: "X-Forwarded-Email",
1782 Values: []options.HeaderValue{
1784 ClaimSource: &options.ClaimSource{
1792 opts.InjectResponseHeaders = []options.Header{
1794 Name: "Authorization",
1795 Values: []options.HeaderValue{
1797 ClaimSource: &options.ClaimSource{
1805 Name: "X-Auth-Request-User",
1806 Values: []options.HeaderValue{
1808 ClaimSource: &options.ClaimSource{
1815 Name: "X-Auth-Request-Email",
1816 Values: []options.HeaderValue{
1818 ClaimSource: &options.ClaimSource{
1825 opts.SkipJwtBearerTokens = true
1826 opts.SetJWTBearerVerifiers(append(opts.GetJWTBearerVerifiers(), internalVerifier))
1831 tp, _ := test.proxy.provider.(*TestProvider)
1832 tp.GroupValidator = func(s string) bool {
1836 authHeader := fmt.Sprintf("Bearer %s", goodJwt)
1837 test.req.Header = map[string][]string{
1838 "Authorization": {authHeader},
1841 test.proxy.ServeHTTP(test.rw, test.req)
1842 if test.rw.Code >= 400 {
1843 t.Fatalf("expected 3xx got %d", test.rw.Code)
1846 // Check PassAuthorization, should overwrite Basic header
1847 assert.Equal(t, test.req.Header.Get("Authorization"), authHeader)
1848 assert.Equal(t, test.req.Header.Get("X-Forwarded-User"), "1234567890")
1849 assert.Equal(t, test.req.Header.Get("X-Forwarded-Email"), "john@example.com")
1851 // SetAuthorization and SetXAuthRequest
1852 assert.Equal(t, test.rw.Header().Get("Authorization"), authHeader)
1853 assert.Equal(t, test.rw.Header().Get("X-Auth-Request-User"), "1234567890")
1854 assert.Equal(t, test.rw.Header().Get("X-Auth-Request-Email"), "john@example.com")
1857func Test_prepareNoCache(t *testing.T) {
1858 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1861 mux := http.NewServeMux()
1862 mux.Handle("/", handler)
1864 rec := httptest.NewRecorder()
1865 req := httptest.NewRequest(http.MethodGet, "/", nil)
1866 mux.ServeHTTP(rec, req)
1868 for k, v := range noCacheHeaders {
1869 assert.Equal(t, rec.Header().Get(k), v)
1873func Test_noCacheHeaders(t *testing.T) {
1874 upstreamServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1875 _, err := w.Write([]byte("upstream"))
1880 t.Cleanup(upstreamServer.Close)
1882 opts := baseTestOptions()
1883 opts.UpstreamServers = options.UpstreamConfig{
1884 Upstreams: []options.Upstream{
1886 ID: upstreamServer.URL,
1888 URI: upstreamServer.URL,
1892 opts.SkipAuthRegex = []string{".*"}
1893 err := validation.Validate(opts)
1894 assert.NoError(t, err)
1895 proxy, err := NewOAuthProxy(opts, func(_ string) bool { return true })
1900 t.Run("not exist in response from upstream", func(t *testing.T) {
1901 rec := httptest.NewRecorder()
1902 req := httptest.NewRequest(http.MethodGet, "/upstream", nil)
1903 proxy.ServeHTTP(rec, req)
1905 assert.Equal(t, http.StatusOK, rec.Code)
1906 assert.Equal(t, "upstream", rec.Body.String())
1908 // checking noCacheHeaders does not exists in response headers from upstream
1909 for k := range noCacheHeaders {
1910 assert.Equal(t, "", rec.Header().Get(k))
1914 t.Run("has no-cache", func(t *testing.T) {
1920 path: "/oauth2/sign_in",
1924 path: "/oauth2/sign_out",
1928 path: "/oauth2/start",
1932 path: "/oauth2/callback",
1936 path: "/oauth2/auth",
1940 path: "/oauth2/userinfo",
1949 for _, tt := range tests {
1950 t.Run(tt.path, func(t *testing.T) {
1951 rec := httptest.NewRecorder()
1952 req := httptest.NewRequest(http.MethodGet, tt.path, nil)
1953 proxy.ServeHTTP(rec, req)
1954 cacheControl := rec.Result().Header.Get("Cache-Control")
1955 if tt.hasNoCache != (strings.Contains(cacheControl, "no-cache")) {
1956 t.Errorf(`unexpected "Cache-Control" header: %s`, cacheControl)
1964func baseTestOptions() *options.Options {
1965 opts := options.NewOptions()
1966 opts.Cookie.Secret = rawCookieSecret
1967 opts.Providers[0].ID = "providerID"
1968 opts.Providers[0].ClientID = clientID
1969 opts.Providers[0].ClientSecret = clientSecret
1970 opts.EmailDomains = []string{"*"}
1972 // Default injected headers for legacy configuration
1973 opts.InjectRequestHeaders = []options.Header{
1975 Name: "Authorization",
1976 Values: []options.HeaderValue{
1978 ClaimSource: &options.ClaimSource{
1980 BasicAuthPassword: &options.SecretSource{
1981 Value: []byte(base64.StdEncoding.EncodeToString([]byte("This is a secure password"))),
1988 Name: "X-Forwarded-User",
1989 Values: []options.HeaderValue{
1991 ClaimSource: &options.ClaimSource{
1998 Name: "X-Forwarded-Email",
1999 Values: []options.HeaderValue{
2001 ClaimSource: &options.ClaimSource{
2012func TestTrustedIPs(t *testing.T) {
2017 realClientIPHeader string
2021 // Check unconfigured behavior.
2025 reverseProxy: false,
2026 realClientIPHeader: "X-Real-IP", // Default value
2027 req: func() *http.Request {
2028 req, _ := http.NewRequest("GET", "/", nil)
2031 expectTrusted: false,
2033 // Check using req.RemoteAddr (Options.ReverseProxy == false).
2035 name: "WithRemoteAddr",
2036 trustedIPs: []string{"127.0.0.1"},
2037 reverseProxy: false,
2038 realClientIPHeader: "X-Real-IP", // Default value
2039 req: func() *http.Request {
2040 req, _ := http.NewRequest("GET", "/", nil)
2041 req.RemoteAddr = "127.0.0.1:43670"
2044 expectTrusted: true,
2046 // Check ignores req.RemoteAddr match when behind a reverse proxy / missing header.
2048 name: "IgnoresRemoteAddrInReverseProxyMode",
2049 trustedIPs: []string{"127.0.0.1"},
2051 realClientIPHeader: "X-Real-IP", // Default value
2052 req: func() *http.Request {
2053 req, _ := http.NewRequest("GET", "/", nil)
2054 req.RemoteAddr = "127.0.0.1:44324"
2057 expectTrusted: false,
2059 // Check successful trusting of localhost in IPv4.
2061 name: "TrustsLocalhostInReverseProxyMode",
2062 trustedIPs: []string{"127.0.0.0/8", "::1"},
2064 realClientIPHeader: "X-Forwarded-For",
2065 req: func() *http.Request {
2066 req, _ := http.NewRequest("GET", "/", nil)
2067 req.Header.Add("X-Forwarded-For", "127.0.0.1")
2070 expectTrusted: true,
2072 // Check successful trusting of localhost in IPv6.
2074 name: "TrustsIP6LocalostInReverseProxyMode",
2075 trustedIPs: []string{"127.0.0.0/8", "::1"},
2077 realClientIPHeader: "X-Forwarded-For",
2078 req: func() *http.Request {
2079 req, _ := http.NewRequest("GET", "/", nil)
2080 req.Header.Add("X-Forwarded-For", "::1")
2083 expectTrusted: true,
2085 // Check does not trust random IPv4 address.
2087 name: "DoesNotTrustRandomIP4Address",
2088 trustedIPs: []string{"127.0.0.0/8", "::1"},
2090 realClientIPHeader: "X-Forwarded-For",
2091 req: func() *http.Request {
2092 req, _ := http.NewRequest("GET", "/", nil)
2093 req.Header.Add("X-Forwarded-For", "12.34.56.78")
2096 expectTrusted: false,
2098 // Check does not trust random IPv6 address.
2100 name: "DoesNotTrustRandomIP6Address",
2101 trustedIPs: []string{"127.0.0.0/8", "::1"},
2103 realClientIPHeader: "X-Forwarded-For",
2104 req: func() *http.Request {
2105 req, _ := http.NewRequest("GET", "/", nil)
2106 req.Header.Add("X-Forwarded-For", "::2")
2109 expectTrusted: false,
2111 // Check respects correct header.
2113 name: "RespectsCorrectHeaderInReverseProxyMode",
2114 trustedIPs: []string{"127.0.0.0/8", "::1"},
2116 realClientIPHeader: "X-Forwarded-For",
2117 req: func() *http.Request {
2118 req, _ := http.NewRequest("GET", "/", nil)
2119 req.Header.Add("X-Real-IP", "::1")
2122 expectTrusted: false,
2124 // Check doesn't trust if garbage is provided.
2126 name: "DoesNotTrustGarbageInReverseProxyMode",
2127 trustedIPs: []string{"127.0.0.0/8", "::1"},
2129 realClientIPHeader: "X-Forwarded-For",
2130 req: func() *http.Request {
2131 req, _ := http.NewRequest("GET", "/", nil)
2132 req.Header.Add("X-Forwarded-For", "adsfljk29242as!!")
2135 expectTrusted: false,
2137 // Check doesn't trust if garbage is provided (no reverse-proxy).
2139 name: "DoesNotTrustGarbage",
2140 trustedIPs: []string{"127.0.0.0/8", "::1"},
2141 reverseProxy: false,
2142 realClientIPHeader: "X-Real-IP",
2143 req: func() *http.Request {
2144 req, _ := http.NewRequest("GET", "/", nil)
2145 req.RemoteAddr = "adsfljk29242as!!"
2148 expectTrusted: false,
2152 for _, tt := range tests {
2153 t.Run(tt.name, func(t *testing.T) {
2154 opts := baseTestOptions()
2155 opts.UpstreamServers = options.UpstreamConfig{
2156 Upstreams: []options.Upstream{
2164 opts.TrustedIPs = tt.trustedIPs
2165 opts.ReverseProxy = tt.reverseProxy
2166 opts.RealClientIPHeader = tt.realClientIPHeader
2167 err := validation.Validate(opts)
2168 assert.NoError(t, err)
2170 proxy, err := NewOAuthProxy(opts, func(string) bool { return true })
2171 assert.NoError(t, err)
2172 rw := httptest.NewRecorder()
2174 proxy.ServeHTTP(rw, tt.req)
2175 if tt.expectTrusted {
2176 assert.Equal(t, 200, rw.Code)
2178 assert.Equal(t, 403, rw.Code)
2184func Test_buildRoutesAllowlist(t *testing.T) {
2185 type expectedAllowedRoute struct {
2190 testCases := []struct {
2192 skipAuthRegex []string
2193 skipAuthRoutes []string
2194 expectedRoutes []expectedAllowedRoute
2198 name: "No skip auth configured",
2199 skipAuthRegex: []string{},
2200 skipAuthRoutes: []string{},
2201 expectedRoutes: []expectedAllowedRoute{},
2205 name: "Only skipAuthRegex configured",
2206 skipAuthRegex: []string{
2208 "^/baz/[0-9]+/thing",
2210 skipAuthRoutes: []string{},
2211 expectedRoutes: []expectedAllowedRoute{
2214 regexString: "^/foo/bar",
2218 regexString: "^/baz/[0-9]+/thing",
2224 name: "Only skipAuthRoutes configured",
2225 skipAuthRegex: []string{},
2226 skipAuthRoutes: []string{
2228 "POST=^/baz/[0-9]+/thing",
2230 "WEIRD=^/methods/are/allowed",
2231 "PATCH=/second/equals?are=handled&just=fine",
2233 expectedRoutes: []expectedAllowedRoute{
2236 regexString: "^/foo/bar",
2240 regexString: "^/baz/[0-9]+/thing",
2244 regexString: "^/all/methods$",
2248 regexString: "^/methods/are/allowed",
2252 regexString: "/second/equals?are=handled&just=fine",
2258 name: "Both skipAuthRegexes and skipAuthRoutes configured",
2259 skipAuthRegex: []string{
2261 "^/baz/[0-9]+/thing/regex",
2263 skipAuthRoutes: []string{
2265 "POST=^/baz/[0-9]+/thing",
2268 expectedRoutes: []expectedAllowedRoute{
2271 regexString: "^/foo/bar/regex",
2275 regexString: "^/baz/[0-9]+/thing/regex",
2279 regexString: "^/foo/bar",
2283 regexString: "^/baz/[0-9]+/thing",
2287 regexString: "^/all/methods$",
2293 name: "Invalid skipAuthRegex entry",
2294 skipAuthRegex: []string{
2296 "^/baz/[0-9]+/thing",
2299 skipAuthRoutes: []string{},
2300 expectedRoutes: []expectedAllowedRoute{},
2304 name: "Invalid skipAuthRoutes entry",
2305 skipAuthRegex: []string{},
2306 skipAuthRoutes: []string{
2308 "POST=^/baz/[0-9]+/thing",
2312 expectedRoutes: []expectedAllowedRoute{},
2317 for _, tc := range testCases {
2318 t.Run(tc.name, func(t *testing.T) {
2319 opts := &options.Options{
2320 SkipAuthRegex: tc.skipAuthRegex,
2321 SkipAuthRoutes: tc.skipAuthRoutes,
2323 routes, err := buildRoutesAllowlist(opts)
2325 assert.Error(t, err)
2328 assert.NoError(t, err)
2330 for i, route := range routes {
2331 assert.Greater(t, len(tc.expectedRoutes), i)
2332 assert.Equal(t, route.method, tc.expectedRoutes[i].method)
2333 assert.Equal(t, route.pathRegex.String(), tc.expectedRoutes[i].regexString)
2339func TestAllowedRequest(t *testing.T) {
2340 upstreamServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2342 _, err := w.Write([]byte("Allowed Request"))
2347 t.Cleanup(upstreamServer.Close)
2349 opts := baseTestOptions()
2350 opts.UpstreamServers = options.UpstreamConfig{
2351 Upstreams: []options.Upstream{
2353 ID: upstreamServer.URL,
2355 URI: upstreamServer.URL,
2359 opts.SkipAuthRegex = []string{
2360 "^/skip/auth/regex$",
2362 opts.SkipAuthRoutes = []string{
2363 "GET=^/skip/auth/routes/get",
2365 err := validation.Validate(opts)
2366 assert.NoError(t, err)
2367 proxy, err := NewOAuthProxy(opts, func(_ string) bool { return true })
2372 testCases := []struct {
2379 name: "Regex GET allowed",
2381 url: "/skip/auth/regex",
2385 name: "Regex POST allowed ",
2387 url: "/skip/auth/regex",
2391 name: "Regex denied",
2393 url: "/wrong/denied",
2397 name: "Route allowed",
2399 url: "/skip/auth/routes/get",
2403 name: "Route denied with wrong method",
2405 url: "/skip/auth/routes/get",
2409 name: "Route denied with wrong path",
2411 url: "/skip/auth/routes/wrong/path",
2416 for _, tc := range testCases {
2417 t.Run(tc.name, func(t *testing.T) {
2418 req, err := http.NewRequest(tc.method, tc.url, nil)
2419 assert.NoError(t, err)
2420 assert.Equal(t, tc.allowed, proxy.isAllowedRoute(req))
2422 rw := httptest.NewRecorder()
2423 proxy.ServeHTTP(rw, req)
2426 assert.Equal(t, 200, rw.Code)
2427 assert.Equal(t, "Allowed Request", rw.Body.String())
2429 assert.Equal(t, 403, rw.Code)
2435func TestProxyAllowedGroups(t *testing.T) {
2438 allowedGroups []string
2440 expectUnauthorized bool
2442 {"NoAllowedGroups", []string{}, []string{}, false},
2443 {"NoAllowedGroupsUserHasGroups", []string{}, []string{"a", "b"}, false},
2444 {"UserInAllowedGroup", []string{"a"}, []string{"a", "b"}, false},
2445 {"UserNotInAllowedGroup", []string{"a"}, []string{"c"}, true},
2448 for _, tt := range tests {
2449 t.Run(tt.name, func(t *testing.T) {
2450 emailAddress := "test"
2451 created := time.Now()
2453 session := &sessions.SessionState{
2455 Email: emailAddress,
2456 AccessToken: "oauth_token",
2457 CreatedAt: &created,
2460 upstreamServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2463 t.Cleanup(upstreamServer.Close)
2465 test, err := NewProcessCookieTestWithOptionsModifiers(func(opts *options.Options) {
2466 opts.Providers[0].AllowedGroups = tt.allowedGroups
2467 opts.UpstreamServers = options.UpstreamConfig{
2468 Upstreams: []options.Upstream{
2470 ID: upstreamServer.URL,
2472 URI: upstreamServer.URL,
2481 test.req, _ = http.NewRequest("GET", "/", nil)
2483 test.req.Header.Add("accept", applicationJSON)
2484 err = test.SaveSession(session)
2485 assert.NoError(t, err)
2486 test.proxy.ServeHTTP(test.rw, test.req)
2488 if tt.expectUnauthorized {
2489 assert.Equal(t, http.StatusForbidden, test.rw.Code)
2491 assert.Equal(t, http.StatusOK, test.rw.Code)
2497func TestAuthOnlyAllowedGroups(t *testing.T) {
2498 testCases := []struct {
2500 allowedGroups []string
2503 expectedStatusCode int
2506 name: "NoAllowedGroups",
2507 allowedGroups: []string{},
2510 expectedStatusCode: http.StatusAccepted,
2513 name: "NoAllowedGroupsUserHasGroups",
2514 allowedGroups: []string{},
2515 groups: []string{"a", "b"},
2517 expectedStatusCode: http.StatusAccepted,
2520 name: "UserInAllowedGroup",
2521 allowedGroups: []string{"a"},
2522 groups: []string{"a", "b"},
2524 expectedStatusCode: http.StatusAccepted,
2527 name: "UserNotInAllowedGroup",
2528 allowedGroups: []string{"a"},
2529 groups: []string{"c"},
2531 expectedStatusCode: http.StatusUnauthorized,
2534 name: "UserInQuerystringGroup",
2535 allowedGroups: []string{"a", "b"},
2536 groups: []string{"a", "c"},
2537 querystring: "?allowed_groups=a",
2538 expectedStatusCode: http.StatusAccepted,
2541 name: "UserInMultiParamQuerystringGroup",
2542 allowedGroups: []string{"a", "b"},
2543 groups: []string{"b"},
2544 querystring: "?allowed_groups=a&allowed_groups=b,d",
2545 expectedStatusCode: http.StatusAccepted,
2548 name: "UserInOnlyQuerystringGroup",
2549 allowedGroups: []string{},
2550 groups: []string{"a", "c"},
2551 querystring: "?allowed_groups=a,b",
2552 expectedStatusCode: http.StatusAccepted,
2555 name: "UserInDelimitedQuerystringGroup",
2556 allowedGroups: []string{"a", "b", "c"},
2557 groups: []string{"c"},
2558 querystring: "?allowed_groups=a,c",
2559 expectedStatusCode: http.StatusAccepted,
2562 name: "UserNotInQuerystringGroup",
2563 allowedGroups: []string{},
2564 groups: []string{"c"},
2565 querystring: "?allowed_groups=a,b",
2566 expectedStatusCode: http.StatusForbidden,
2569 name: "UserInConfigGroupNotInQuerystringGroup",
2570 allowedGroups: []string{"a", "b", "c"},
2571 groups: []string{"c"},
2572 querystring: "?allowed_groups=a,b",
2573 expectedStatusCode: http.StatusForbidden,
2576 name: "UserInQuerystringGroupNotInConfigGroup",
2577 allowedGroups: []string{"a", "b"},
2578 groups: []string{"c"},
2579 querystring: "?allowed_groups=b,c",
2580 expectedStatusCode: http.StatusUnauthorized,
2584 for _, tc := range testCases {
2585 t.Run(tc.name, func(t *testing.T) {
2586 emailAddress := "test"
2587 created := time.Now()
2589 session := &sessions.SessionState{
2591 Email: emailAddress,
2592 AccessToken: "oauth_token",
2593 CreatedAt: &created,
2596 test, err := NewAuthOnlyEndpointTest(tc.querystring, func(opts *options.Options) {
2597 opts.Providers[0].AllowedGroups = tc.allowedGroups
2603 err = test.SaveSession(session)
2604 assert.NoError(t, err)
2606 test.proxy.ServeHTTP(test.rw, test.req)
2608 assert.Equal(t, tc.expectedStatusCode, test.rw.Code)
2613func TestAuthOnlyAllowedGroupsWithSkipMethods(t *testing.T) {
2614 testCases := []struct {
2620 expectedStatusCode int
2623 name: "UserWithGroupSkipAuthPreflight",
2624 groups: []string{"a", "c"},
2626 ip: "1.2.3.5:43670",
2628 expectedStatusCode: http.StatusAccepted,
2631 name: "UserWithGroupTrustedIp",
2632 groups: []string{"a", "c"},
2634 ip: "1.2.3.4:43670",
2636 expectedStatusCode: http.StatusAccepted,
2639 name: "UserWithoutGroupSkipAuthPreflight",
2640 groups: []string{"c"},
2642 ip: "1.2.3.5:43670",
2644 expectedStatusCode: http.StatusForbidden,
2647 name: "UserWithoutGroupTrustedIp",
2648 groups: []string{"c"},
2650 ip: "1.2.3.4:43670",
2652 expectedStatusCode: http.StatusForbidden,
2655 name: "UserWithoutSessionSkipAuthPreflight",
2657 ip: "1.2.3.5:43670",
2659 expectedStatusCode: http.StatusAccepted,
2662 name: "UserWithoutSessionTrustedIp",
2664 ip: "1.2.3.4:43670",
2666 expectedStatusCode: http.StatusAccepted,
2670 for _, tc := range testCases {
2671 t.Run(tc.name, func(t *testing.T) {
2672 test, err := NewAuthOnlyEndpointTest("?allowed_groups=a,b", func(opts *options.Options) {
2673 opts.SkipAuthPreflight = true
2674 opts.TrustedIPs = []string{"1.2.3.4"}
2680 test.req.Method = tc.method
2681 test.req.RemoteAddr = tc.ip
2684 created := time.Now()
2685 session := &sessions.SessionState{
2688 AccessToken: "oauth_token",
2689 CreatedAt: &created,
2691 err = test.SaveSession(session)
2693 assert.NoError(t, err)
2695 test.proxy.ServeHTTP(test.rw, test.req)
2697 assert.Equal(t, tc.expectedStatusCode, test.rw.Code)
2702func TestAuthOnlyAllowedEmailDomains(t *testing.T) {
2703 testCases := []struct {
2707 expectedStatusCode int
2710 name: "NotEmailRestriction",
2711 email: "toto@example.com",
2713 expectedStatusCode: http.StatusAccepted,
2716 name: "UserInAllowedEmailDomain",
2717 email: "toto@example.com",
2718 querystring: "?allowed_email_domains=example.com",
2719 expectedStatusCode: http.StatusAccepted,
2722 name: "UserNotInAllowedEmailDomain",
2723 email: "toto@example.com",
2724 querystring: "?allowed_email_domains=a.example.com",
2725 expectedStatusCode: http.StatusForbidden,
2728 name: "UserInAllowedEmailDomains",
2729 email: "toto@example.com",
2730 querystring: "?allowed_email_domains=a.example.com,b.example.com",
2731 expectedStatusCode: http.StatusForbidden,
2734 name: "UserInAllowedEmailDomains",
2735 email: "toto@example.com",
2736 querystring: "?allowed_email_domains=a.example.com,example.com",
2737 expectedStatusCode: http.StatusAccepted,
2740 name: "UserInAllowedEmailDomainWildcard",
2741 email: "toto@foo.example.com",
2742 querystring: "?allowed_email_domains=*.example.com",
2743 expectedStatusCode: http.StatusAccepted,
2746 name: "UserNotInAllowedEmailDomainWildcard",
2747 email: "toto@example.com",
2748 querystring: "?allowed_email_domains=*.a.example.com",
2749 expectedStatusCode: http.StatusForbidden,
2752 name: "UserInAllowedEmailDomainsWildcard",
2753 email: "toto@example.com",
2754 querystring: "?allowed_email_domains=*.a.example.com,*.b.example.com",
2755 expectedStatusCode: http.StatusForbidden,
2758 name: "UserInAllowedEmailDomainsWildcard",
2759 email: "toto@c.example.com",
2760 querystring: "?allowed_email_domains=a.b.c.example.com,*.c.example.com",
2761 expectedStatusCode: http.StatusAccepted,
2765 for _, tc := range testCases {
2766 t.Run(tc.name, func(t *testing.T) {
2767 groups := []string{}
2769 created := time.Now()
2771 session := &sessions.SessionState{
2774 AccessToken: "oauth_token",
2775 CreatedAt: &created,
2778 test, err := NewAuthOnlyEndpointTest(tc.querystring, func(opts *options.Options) {})
2783 err = test.SaveSession(session)
2784 assert.NoError(t, err)
2786 test.proxy.ServeHTTP(test.rw, test.req)
2788 assert.Equal(t, tc.expectedStatusCode, test.rw.Code)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list