git.iko.soy/typeable/oauth2-proxy
on v4.1.0 help sign in
typeable/oauth2-proxy/oauthproxy_test.go
1392 lines 42 kB history raw
1package main
3import (
4 "context"
5 "crypto"
6 "encoding/base64"
7 "fmt"
8 "io"
9 "io/ioutil"
10 "net"
11 "net/http"
12 "net/http/httptest"
13 "net/url"
14 "regexp"
15 "strings"
16 "testing"
17 "time"
19 "github.com/coreos/go-oidc"
20 "github.com/mbland/hmacauth"
21 "github.com/pusher/oauth2_proxy/pkg/apis/sessions"
22 "github.com/pusher/oauth2_proxy/pkg/logger"
23 "github.com/pusher/oauth2_proxy/pkg/sessions/cookie"
24 "github.com/pusher/oauth2_proxy/providers"
25 "github.com/stretchr/testify/assert"
26 "github.com/stretchr/testify/require"
27 "golang.org/x/net/websocket"
30func init() {
31 logger.SetFlags(logger.Lshortfile)
35type WebSocketOrRestHandler struct {
36 restHandler http.Handler
37 wsHandler http.Handler
40func (h *WebSocketOrRestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
41 if r.Header.Get("Upgrade") == "websocket" {
42 h.wsHandler.ServeHTTP(w, r)
43 } else {
44 h.restHandler.ServeHTTP(w, r)
45 }
48func TestWebSocketProxy(t *testing.T) {
49 handler := WebSocketOrRestHandler{
50 restHandler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
51 w.WriteHeader(200)
52 hostname, _, _ := net.SplitHostPort(r.Host)
53 w.Write([]byte(hostname))
54 }),
55 wsHandler: websocket.Handler(func(ws *websocket.Conn) {
56 defer ws.Close()
57 var data []byte
58 err := websocket.Message.Receive(ws, &data)
59 if err != nil {
60 t.Fatalf("err %s", err)
61 return
62 }
63 err = websocket.Message.Send(ws, data)
64 if err != nil {
65 t.Fatalf("err %s", err)
66 }
67 return
68 }),
69 }
70 backend := httptest.NewServer(&handler)
71 defer backend.Close()
73 backendURL, _ := url.Parse(backend.URL)
75 options := NewOptions()
76 var auth hmacauth.HmacAuth
77 options.PassHostHeader = true
78 proxyHandler := NewWebSocketOrRestReverseProxy(backendURL, options, auth)
79 frontend := httptest.NewServer(proxyHandler)
80 defer frontend.Close()
82 frontendURL, _ := url.Parse(frontend.URL)
83 frontendWSURL := "ws://" + frontendURL.Host + "/"
85 ws, err := websocket.Dial(frontendWSURL, "", "http://localhost/")
86 if err != nil {
87 t.Fatalf("err %s", err)
88 }
89 request := []byte("hello, world!")
90 err = websocket.Message.Send(ws, request)
91 if err != nil {
92 t.Fatalf("err %s", err)
93 }
94 var response = make([]byte, 1024)
95 websocket.Message.Receive(ws, &response)
96 if err != nil {
97 t.Fatalf("err %s", err)
98 }
99 if g, e := string(request), string(response); g != e {
100 t.Errorf("got body %q; expected %q", g, e)
101 }
103 getReq, _ := http.NewRequest("GET", frontend.URL, nil)
104 res, _ := http.DefaultClient.Do(getReq)
105 bodyBytes, _ := ioutil.ReadAll(res.Body)
106 backendHostname, _, _ := net.SplitHostPort(backendURL.Host)
107 if g, e := string(bodyBytes), backendHostname; g != e {
108 t.Errorf("got body %q; expected %q", g, e)
109 }
112func TestNewReverseProxy(t *testing.T) {
113 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
114 w.WriteHeader(200)
115 hostname, _, _ := net.SplitHostPort(r.Host)
116 w.Write([]byte(hostname))
117 }))
118 defer backend.Close()
120 backendURL, _ := url.Parse(backend.URL)
121 backendHostname, backendPort, _ := net.SplitHostPort(backendURL.Host)
122 backendHost := net.JoinHostPort(backendHostname, backendPort)
123 proxyURL, _ := url.Parse(backendURL.Scheme + "://" + backendHost + "/")
125 proxyHandler := NewReverseProxy(proxyURL, &Options{FlushInterval: time.Second})
126 setProxyUpstreamHostHeader(proxyHandler, proxyURL)
127 frontend := httptest.NewServer(proxyHandler)
128 defer frontend.Close()
130 getReq, _ := http.NewRequest("GET", frontend.URL, nil)
131 res, _ := http.DefaultClient.Do(getReq)
132 bodyBytes, _ := ioutil.ReadAll(res.Body)
133 if g, e := string(bodyBytes), backendHostname; g != e {
134 t.Errorf("got body %q; expected %q", g, e)
135 }
138func TestEncodedSlashes(t *testing.T) {
139 var seen string
140 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
141 w.WriteHeader(200)
142 seen = r.RequestURI
143 }))
144 defer backend.Close()
146 b, _ := url.Parse(backend.URL)
147 proxyHandler := NewReverseProxy(b, &Options{FlushInterval: time.Second})
148 setProxyDirector(proxyHandler)
149 frontend := httptest.NewServer(proxyHandler)
150 defer frontend.Close()
152 f, _ := url.Parse(frontend.URL)
153 encodedPath := "/a%2Fb/?c=1"
154 getReq := &http.Request{URL: &url.URL{Scheme: "http", Host: f.Host, Opaque: encodedPath}}
155 _, err := http.DefaultClient.Do(getReq)
156 if err != nil {
157 t.Fatalf("err %s", err)
158 }
159 if seen != encodedPath {
160 t.Errorf("got bad request %q expected %q", seen, encodedPath)
161 }
164func TestRobotsTxt(t *testing.T) {
165 opts := NewOptions()
166 opts.ClientID = "asdlkjx"
167 opts.ClientSecret = "alkgks"
168 opts.CookieSecret = "asdkugkj"
169 opts.Validate()
171 proxy := NewOAuthProxy(opts, func(string) bool { return true })
172 rw := httptest.NewRecorder()
173 req, _ := http.NewRequest("GET", "/robots.txt", nil)
174 proxy.ServeHTTP(rw, req)
175 assert.Equal(t, 200, rw.Code)
176 assert.Equal(t, "User-agent: *\nDisallow: /", rw.Body.String())
179func TestIsValidRedirect(t *testing.T) {
180 opts := NewOptions()
181 opts.ClientID = "skdlfj"
182 opts.ClientSecret = "fgkdsgj"
183 opts.CookieSecret = "ljgiogbj"
184 // Should match domains that are exactly foo.bar and any subdomain of bar.foo
185 opts.WhitelistDomains = []string{"foo.bar", ".bar.foo"}
186 opts.Validate()
188 proxy := NewOAuthProxy(opts, func(string) bool { return true })
190 noRD := proxy.IsValidRedirect("")
191 assert.Equal(t, false, noRD)
193 singleSlash := proxy.IsValidRedirect("/redirect")
194 assert.Equal(t, true, singleSlash)
196 doubleSlash := proxy.IsValidRedirect("//redirect")
197 assert.Equal(t, false, doubleSlash)
199 validHTTP := proxy.IsValidRedirect("http://foo.bar/redirect")
200 assert.Equal(t, true, validHTTP)
202 validHTTPS := proxy.IsValidRedirect("https://foo.bar/redirect")
203 assert.Equal(t, true, validHTTPS)
205 invalidHTTPSubdomain := proxy.IsValidRedirect("http://baz.foo.bar/redirect")
206 assert.Equal(t, false, invalidHTTPSubdomain)
208 invalidHTTPSSubdomain := proxy.IsValidRedirect("https://baz.foo.bar/redirect")
209 assert.Equal(t, false, invalidHTTPSSubdomain)
211 validHTTPSubdomain := proxy.IsValidRedirect("http://baz.bar.foo/redirect")
212 assert.Equal(t, true, validHTTPSubdomain)
214 validHTTPSSubdomain := proxy.IsValidRedirect("https://baz.bar.foo/redirect")
215 assert.Equal(t, true, validHTTPSSubdomain)
217 invalidHTTP1 := proxy.IsValidRedirect("http://foo.bar.evil.corp/redirect")
218 assert.Equal(t, false, invalidHTTP1)
220 invalidHTTPS1 := proxy.IsValidRedirect("https://foo.bar.evil.corp/redirect")
221 assert.Equal(t, false, invalidHTTPS1)
223 invalidHTTP2 := proxy.IsValidRedirect("http://evil.corp/redirect?rd=foo.bar")
224 assert.Equal(t, false, invalidHTTP2)
226 invalidHTTPS2 := proxy.IsValidRedirect("https://evil.corp/redirect?rd=foo.bar")
227 assert.Equal(t, false, invalidHTTPS2)
230type TestProvider struct {
231 *providers.ProviderData
232 EmailAddress string
233 ValidToken bool
234 GroupValidator func(string) bool
237func NewTestProvider(providerURL *url.URL, emailAddress string) *TestProvider {
238 return &TestProvider{
239 ProviderData: &providers.ProviderData{
240 ProviderName: "Test Provider",
241 LoginURL: &url.URL{
242 Scheme: "http",
243 Host: providerURL.Host,
244 Path: "/oauth/authorize",
245 },
246 RedeemURL: &url.URL{
247 Scheme: "http",
248 Host: providerURL.Host,
249 Path: "/oauth/token",
250 },
251 ProfileURL: &url.URL{
252 Scheme: "http",
253 Host: providerURL.Host,
254 Path: "/api/v1/profile",
255 },
256 Scope: "profile.email",
257 },
258 EmailAddress: emailAddress,
259 GroupValidator: func(s string) bool {
260 return true
261 },
262 }
265func (tp *TestProvider) GetEmailAddress(session *sessions.SessionState) (string, error) {
266 return tp.EmailAddress, nil
269func (tp *TestProvider) ValidateSessionState(session *sessions.SessionState) bool {
270 return tp.ValidToken
273func (tp *TestProvider) ValidateGroup(email string) bool {
274 if tp.GroupValidator != nil {
275 return tp.GroupValidator(email)
276 }
277 return true
280func TestBasicAuthPassword(t *testing.T) {
281 providerServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
282 logger.Printf("%#v", r)
283 var payload string
284 switch r.URL.Path {
285 case "/oauth/token":
286 payload = `{"access_token": "my_auth_token"}`
287 default:
288 payload = r.Header.Get("Authorization")
289 if payload == "" {
290 payload = "No Authorization header found."
291 }
292 }
293 w.WriteHeader(200)
294 w.Write([]byte(payload))
295 }))
296 opts := NewOptions()
297 opts.Upstreams = append(opts.Upstreams, providerServer.URL)
298 // The CookieSecret must be 32 bytes in order to create the AES
299 // cipher.
300 opts.CookieSecret = "xyzzyplughxyzzyplughxyzzyplughxp"
301 opts.ClientID = "dlgkj"
302 opts.ClientSecret = "alkgret"
303 opts.CookieSecure = false
304 opts.PassBasicAuth = true
305 opts.PassUserHeaders = true
306 opts.BasicAuthPassword = "This is a secure password"
307 opts.Validate()
309 providerURL, _ := url.Parse(providerServer.URL)
310 const emailAddress = "john.doe@example.com"
312 opts.provider = NewTestProvider(providerURL, emailAddress)
313 proxy := NewOAuthProxy(opts, func(email string) bool {
314 return email == emailAddress
315 })
317 rw := httptest.NewRecorder()
318 req, _ := http.NewRequest("GET", "/oauth2/callback?code=callback_code&state=nonce:",
319 strings.NewReader(""))
320 req.AddCookie(proxy.MakeCSRFCookie(req, "nonce", proxy.CookieExpire, time.Now()))
321 proxy.ServeHTTP(rw, req)
322 if rw.Code >= 400 {
323 t.Fatalf("expected 3xx got %d", rw.Code)
324 }
325 cookie := rw.HeaderMap["Set-Cookie"][1]
327 cookieName := proxy.CookieName
328 var value string
329 keyPrefix := cookieName + "="
331 for _, field := range strings.Split(cookie, "; ") {
332 value = strings.TrimPrefix(field, keyPrefix)
333 if value != field {
334 break
335 } else {
336 value = ""
337 }
338 }
340 req, _ = http.NewRequest("GET", "/", strings.NewReader(""))
341 req.AddCookie(&http.Cookie{
342 Name: cookieName,
343 Value: value,
344 Path: "/",
345 Expires: time.Now().Add(time.Duration(24)),
346 HttpOnly: true,
347 })
348 req.AddCookie(proxy.MakeCSRFCookie(req, "nonce", proxy.CookieExpire, time.Now()))
350 rw = httptest.NewRecorder()
351 proxy.ServeHTTP(rw, req)
353 // The username in the basic auth credentials is expected to be equal to the email address from the
354 // auth response, so we use the same variable here.
355 expectedHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(emailAddress+":"+opts.BasicAuthPassword))
356 assert.Equal(t, expectedHeader, rw.Body.String())
357 providerServer.Close()
360type PassAccessTokenTest struct {
361 providerServer *httptest.Server
362 proxy *OAuthProxy
363 opts *Options
366type PassAccessTokenTestOptions struct {
367 PassAccessToken bool
368 ProxyUpstream string
371func NewPassAccessTokenTest(opts PassAccessTokenTestOptions) *PassAccessTokenTest {
372 t := &PassAccessTokenTest{}
374 t.providerServer = httptest.NewServer(
375 http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
376 var payload string
377 switch r.URL.Path {
378 case "/oauth/token":
379 payload = `{"access_token": "my_auth_token"}`
380 default:
381 payload = r.Header.Get("X-Forwarded-Access-Token")
382 if payload == "" {
383 payload = "No access token found."
384 }
385 }
386 w.WriteHeader(200)
387 w.Write([]byte(payload))
388 }))
390 t.opts = NewOptions()
391 t.opts.Upstreams = append(t.opts.Upstreams, t.providerServer.URL)
392 if opts.ProxyUpstream != "" {
393 t.opts.Upstreams = append(t.opts.Upstreams, opts.ProxyUpstream)
394 }
395 // The CookieSecret must be 32 bytes in order to create the AES
396 // cipher.
397 t.opts.CookieSecret = "xyzzyplughxyzzyplughxyzzyplughxp"
398 t.opts.ClientID = "slgkj"
399 t.opts.ClientSecret = "gfjgojl"
400 t.opts.CookieSecure = false
401 t.opts.PassAccessToken = opts.PassAccessToken
402 t.opts.Validate()
404 providerURL, _ := url.Parse(t.providerServer.URL)
405 const emailAddress = "michael.bland@gsa.gov"
407 t.opts.provider = NewTestProvider(providerURL, emailAddress)
408 t.proxy = NewOAuthProxy(t.opts, func(email string) bool {
409 return email == emailAddress
410 })
411 return t
414func (patTest *PassAccessTokenTest) Close() {
415 patTest.providerServer.Close()
418func (patTest *PassAccessTokenTest) getCallbackEndpoint() (httpCode int,
419 cookie string) {
420 rw := httptest.NewRecorder()
421 req, err := http.NewRequest("GET", "/oauth2/callback?code=callback_code&state=nonce:",
422 strings.NewReader(""))
423 if err != nil {
424 return 0, ""
425 }
426 req.AddCookie(patTest.proxy.MakeCSRFCookie(req, "nonce", time.Hour, time.Now()))
427 patTest.proxy.ServeHTTP(rw, req)
428 return rw.Code, rw.HeaderMap["Set-Cookie"][1]
431// getEndpointWithCookie makes a requests againt the oauthproxy with passed requestPath
432// and cookie and returns body and status code.
433func (patTest *PassAccessTokenTest) getEndpointWithCookie(cookie string, endpoint string) (httpCode int, accessToken string) {
434 cookieName := patTest.proxy.CookieName
435 var value string
436 keyPrefix := cookieName + "="
438 for _, field := range strings.Split(cookie, "; ") {
439 value = strings.TrimPrefix(field, keyPrefix)
440 if value != field {
441 break
442 } else {
443 value = ""
444 }
445 }
446 if value == "" {
447 return 0, ""
448 }
450 req, err := http.NewRequest("GET", endpoint, strings.NewReader(""))
451 if err != nil {
452 return 0, ""
453 }
454 req.AddCookie(&http.Cookie{
455 Name: cookieName,
456 Value: value,
457 Path: "/",
458 Expires: time.Now().Add(time.Duration(24)),
459 HttpOnly: true,
460 })
462 rw := httptest.NewRecorder()
463 patTest.proxy.ServeHTTP(rw, req)
464 return rw.Code, rw.Body.String()
467func TestForwardAccessTokenUpstream(t *testing.T) {
468 patTest := NewPassAccessTokenTest(PassAccessTokenTestOptions{
469 PassAccessToken: true,
470 })
471 defer patTest.Close()
473 // A successful validation will redirect and set the auth cookie.
474 code, cookie := patTest.getCallbackEndpoint()
475 if code != 302 {
476 t.Fatalf("expected 302; got %d", code)
477 }
478 assert.NotEqual(t, nil, cookie)
480 // Now we make a regular request; the access_token from the cookie is
481 // forwarded as the "X-Forwarded-Access-Token" header. The token is
482 // read by the test provider server and written in the response body.
483 code, payload := patTest.getEndpointWithCookie(cookie, "/")
484 if code != 200 {
485 t.Fatalf("expected 200; got %d", code)
486 }
487 assert.Equal(t, "my_auth_token", payload)
490func TestStaticProxyUpstream(t *testing.T) {
491 patTest := NewPassAccessTokenTest(PassAccessTokenTestOptions{
492 PassAccessToken: true,
493 ProxyUpstream: "static://200/static-proxy",
494 })
496 defer patTest.Close()
498 // A successful validation will redirect and set the auth cookie.
499 code, cookie := patTest.getCallbackEndpoint()
500 if code != 302 {
501 t.Fatalf("expected 302; got %d", code)
502 }
503 assert.NotEqual(t, nil, cookie)
505 // Now we make a regular request againts the upstream proxy; And validate
506 // the returned status code through the static proxy.
507 code, payload := patTest.getEndpointWithCookie(cookie, "/static-proxy")
508 if code != 200 {
509 t.Fatalf("expected 200; got %d", code)
510 }
511 assert.Equal(t, "Authenticated", payload)
514func TestDoNotForwardAccessTokenUpstream(t *testing.T) {
515 patTest := NewPassAccessTokenTest(PassAccessTokenTestOptions{
516 PassAccessToken: false,
517 })
518 defer patTest.Close()
520 // A successful validation will redirect and set the auth cookie.
521 code, cookie := patTest.getCallbackEndpoint()
522 if code != 302 {
523 t.Fatalf("expected 302; got %d", code)
524 }
525 assert.NotEqual(t, nil, cookie)
527 // Now we make a regular request, but the access token header should
528 // not be present.
529 code, payload := patTest.getEndpointWithCookie(cookie, "/")
530 if code != 200 {
531 t.Fatalf("expected 200; got %d", code)
532 }
533 assert.Equal(t, "No access token found.", payload)
536type SignInPageTest struct {
537 opts *Options
538 proxy *OAuthProxy
539 signInRegexp *regexp.Regexp
540 signInProviderRegexp *regexp.Regexp
543const signInRedirectPattern = `<input type="hidden" name="rd" value="(.*)">`
544const signInSkipProvider = `>Found<`
546func NewSignInPageTest(skipProvider bool) *SignInPageTest {
547 var sipTest SignInPageTest
549 sipTest.opts = NewOptions()
550 sipTest.opts.CookieSecret = "adklsj2"
551 sipTest.opts.ClientID = "lkdgj"
552 sipTest.opts.ClientSecret = "sgiufgoi"
553 sipTest.opts.SkipProviderButton = skipProvider
554 sipTest.opts.Validate()
556 sipTest.proxy = NewOAuthProxy(sipTest.opts, func(email string) bool {
557 return true
558 })
559 sipTest.signInRegexp = regexp.MustCompile(signInRedirectPattern)
560 sipTest.signInProviderRegexp = regexp.MustCompile(signInSkipProvider)
562 return &sipTest
565func (sipTest *SignInPageTest) GetEndpoint(endpoint string) (int, string) {
566 rw := httptest.NewRecorder()
567 req, _ := http.NewRequest("GET", endpoint, strings.NewReader(""))
568 sipTest.proxy.ServeHTTP(rw, req)
569 return rw.Code, rw.Body.String()
572func TestSignInPageIncludesTargetRedirect(t *testing.T) {
573 sipTest := NewSignInPageTest(false)
574 const endpoint = "/some/random/endpoint"
576 code, body := sipTest.GetEndpoint(endpoint)
577 assert.Equal(t, 403, code)
579 match := sipTest.signInRegexp.FindStringSubmatch(body)
580 if match == nil {
581 t.Fatal("Did not find pattern in body: " +
582 signInRedirectPattern + "\nBody:\n" + body)
583 }
584 if match[1] != endpoint {
585 t.Fatal(`expected redirect to "` + endpoint +
586 `", but was "` + match[1] + `"`)
587 }
590func TestSignInPageDirectAccessRedirectsToRoot(t *testing.T) {
591 sipTest := NewSignInPageTest(false)
592 code, body := sipTest.GetEndpoint("/oauth2/sign_in")
593 assert.Equal(t, 200, code)
595 match := sipTest.signInRegexp.FindStringSubmatch(body)
596 if match == nil {
597 t.Fatal("Did not find pattern in body: " +
598 signInRedirectPattern + "\nBody:\n" + body)
599 }
600 if match[1] != "/" {
601 t.Fatal(`expected redirect to "/", but was "` + match[1] + `"`)
602 }
605func TestSignInPageSkipProvider(t *testing.T) {
606 sipTest := NewSignInPageTest(true)
607 const endpoint = "/some/random/endpoint"
609 code, body := sipTest.GetEndpoint(endpoint)
610 assert.Equal(t, 302, code)
612 match := sipTest.signInProviderRegexp.FindStringSubmatch(body)
613 if match == nil {
614 t.Fatal("Did not find pattern in body: " +
615 signInSkipProvider + "\nBody:\n" + body)
616 }
619func TestSignInPageSkipProviderDirect(t *testing.T) {
620 sipTest := NewSignInPageTest(true)
621 const endpoint = "/sign_in"
623 code, body := sipTest.GetEndpoint(endpoint)
624 assert.Equal(t, 302, code)
626 match := sipTest.signInProviderRegexp.FindStringSubmatch(body)
627 if match == nil {
628 t.Fatal("Did not find pattern in body: " +
629 signInSkipProvider + "\nBody:\n" + body)
630 }
633type ProcessCookieTest struct {
634 opts *Options
635 proxy *OAuthProxy
636 rw *httptest.ResponseRecorder
637 req *http.Request
638 provider TestProvider
639 responseCode int
640 validateUser bool
643type ProcessCookieTestOpts struct {
644 providerValidateCookieResponse bool
647type OptionsModifier func(*Options)
649func NewProcessCookieTest(opts ProcessCookieTestOpts, modifiers ...OptionsModifier) *ProcessCookieTest {
650 var pcTest ProcessCookieTest
652 pcTest.opts = NewOptions()
653 for _, modifier := range modifiers {
654 modifier(pcTest.opts)
655 }
656 pcTest.opts.ClientID = "asdfljk"
657 pcTest.opts.ClientSecret = "lkjfdsig"
658 pcTest.opts.CookieSecret = "0123456789abcdefabcd"
659 // First, set the CookieRefresh option so proxy.AesCipher is created,
660 // needed to encrypt the access_token.
661 pcTest.opts.CookieRefresh = time.Hour
662 pcTest.opts.Validate()
664 pcTest.proxy = NewOAuthProxy(pcTest.opts, func(email string) bool {
665 return pcTest.validateUser
666 })
667 pcTest.proxy.provider = &TestProvider{
668 ValidToken: opts.providerValidateCookieResponse,
669 }
671 // Now, zero-out proxy.CookieRefresh for the cases that don't involve
672 // access_token validation.
673 pcTest.proxy.CookieRefresh = time.Duration(0)
674 pcTest.rw = httptest.NewRecorder()
675 pcTest.req, _ = http.NewRequest("GET", "/", strings.NewReader(""))
676 pcTest.validateUser = true
677 return &pcTest
680func NewProcessCookieTestWithDefaults() *ProcessCookieTest {
681 return NewProcessCookieTest(ProcessCookieTestOpts{
682 providerValidateCookieResponse: true,
683 })
686func NewProcessCookieTestWithOptionsModifiers(modifiers ...OptionsModifier) *ProcessCookieTest {
687 return NewProcessCookieTest(ProcessCookieTestOpts{
688 providerValidateCookieResponse: true,
689 }, modifiers...)
692func (p *ProcessCookieTest) SaveSession(s *sessions.SessionState) error {
693 err := p.proxy.SaveSession(p.rw, p.req, s)
694 if err != nil {
695 return err
696 }
697 for _, cookie := range p.rw.Result().Cookies() {
698 p.req.AddCookie(cookie)
699 }
700 return nil
703func (p *ProcessCookieTest) LoadCookiedSession() (*sessions.SessionState, error) {
704 return p.proxy.LoadCookiedSession(p.req)
707func TestLoadCookiedSession(t *testing.T) {
708 pcTest := NewProcessCookieTestWithDefaults()
710 startSession := &sessions.SessionState{Email: "john.doe@example.com", AccessToken: "my_access_token", CreatedAt: time.Now()}
711 pcTest.SaveSession(startSession)
713 session, err := pcTest.LoadCookiedSession()
714 assert.Equal(t, nil, err)
715 assert.Equal(t, startSession.Email, session.Email)
716 assert.Equal(t, "john.doe@example.com", session.User)
717 assert.Equal(t, startSession.AccessToken, session.AccessToken)
720func TestProcessCookieNoCookieError(t *testing.T) {
721 pcTest := NewProcessCookieTestWithDefaults()
723 session, err := pcTest.LoadCookiedSession()
724 assert.Equal(t, "Cookie \"_oauth2_proxy\" not present", err.Error())
725 if session != nil {
726 t.Errorf("expected nil session. got %#v", session)
727 }
730func TestProcessCookieRefreshNotSet(t *testing.T) {
731 pcTest := NewProcessCookieTestWithOptionsModifiers(func(opts *Options) {
732 opts.CookieExpire = time.Duration(23) * time.Hour
733 })
734 reference := time.Now().Add(time.Duration(-2) * time.Hour)
736 startSession := &sessions.SessionState{Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: reference}
737 pcTest.SaveSession(startSession)
739 session, err := pcTest.LoadCookiedSession()
740 assert.Equal(t, nil, err)
741 if session.Age() < time.Duration(-2)*time.Hour {
742 t.Errorf("cookie too young %v", session.Age())
743 }
744 assert.Equal(t, startSession.Email, session.Email)
747func TestProcessCookieFailIfCookieExpired(t *testing.T) {
748 pcTest := NewProcessCookieTestWithOptionsModifiers(func(opts *Options) {
749 opts.CookieExpire = time.Duration(24) * time.Hour
750 })
751 reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
752 startSession := &sessions.SessionState{Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: reference}
753 pcTest.SaveSession(startSession)
755 session, err := pcTest.LoadCookiedSession()
756 assert.NotEqual(t, nil, err)
757 if session != nil {
758 t.Errorf("expected nil session %#v", session)
759 }
762func TestProcessCookieFailIfRefreshSetAndCookieExpired(t *testing.T) {
763 pcTest := NewProcessCookieTestWithOptionsModifiers(func(opts *Options) {
764 opts.CookieExpire = time.Duration(24) * time.Hour
765 })
766 reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
767 startSession := &sessions.SessionState{Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: reference}
768 pcTest.SaveSession(startSession)
770 pcTest.proxy.CookieRefresh = time.Hour
771 session, err := pcTest.LoadCookiedSession()
772 assert.NotEqual(t, nil, err)
773 if session != nil {
774 t.Errorf("expected nil session %#v", session)
775 }
778func NewUserInfoEndpointTest() *ProcessCookieTest {
779 pcTest := NewProcessCookieTestWithDefaults()
780 pcTest.req, _ = http.NewRequest("GET",
781 pcTest.opts.ProxyPrefix+"/userinfo", nil)
782 return pcTest
785func TestUserInfoEndpointAccepted(t *testing.T) {
786 test := NewUserInfoEndpointTest()
787 startSession := &sessions.SessionState{
788 Email: "john.doe@example.com", AccessToken: "my_access_token"}
789 test.SaveSession(startSession)
791 test.proxy.ServeHTTP(test.rw, test.req)
792 assert.Equal(t, http.StatusOK, test.rw.Code)
793 bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
794 assert.Equal(t, "{\"email\":\"john.doe@example.com\"}\n", string(bodyBytes))
797func TestUserInfoEndpointUnauthorizedOnNoCookieSetError(t *testing.T) {
798 test := NewUserInfoEndpointTest()
800 test.proxy.ServeHTTP(test.rw, test.req)
801 assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
804func NewAuthOnlyEndpointTest(modifiers ...OptionsModifier) *ProcessCookieTest {
805 pcTest := NewProcessCookieTestWithOptionsModifiers(modifiers...)
806 pcTest.req, _ = http.NewRequest("GET",
807 pcTest.opts.ProxyPrefix+"/auth", nil)
808 return pcTest
811func TestAuthOnlyEndpointAccepted(t *testing.T) {
812 test := NewAuthOnlyEndpointTest()
813 startSession := &sessions.SessionState{
814 Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: time.Now()}
815 test.SaveSession(startSession)
817 test.proxy.ServeHTTP(test.rw, test.req)
818 assert.Equal(t, http.StatusAccepted, test.rw.Code)
819 bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
820 assert.Equal(t, "", string(bodyBytes))
823func TestAuthOnlyEndpointUnauthorizedOnNoCookieSetError(t *testing.T) {
824 test := NewAuthOnlyEndpointTest()
826 test.proxy.ServeHTTP(test.rw, test.req)
827 assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
828 bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
829 assert.Equal(t, "unauthorized request\n", string(bodyBytes))
832func TestAuthOnlyEndpointUnauthorizedOnExpiration(t *testing.T) {
833 test := NewAuthOnlyEndpointTest(func(opts *Options) {
834 opts.CookieExpire = time.Duration(24) * time.Hour
835 })
836 reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
837 startSession := &sessions.SessionState{
838 Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: reference}
839 test.SaveSession(startSession)
841 test.proxy.ServeHTTP(test.rw, test.req)
842 assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
843 bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
844 assert.Equal(t, "unauthorized request\n", string(bodyBytes))
847func TestAuthOnlyEndpointUnauthorizedOnEmailValidationFailure(t *testing.T) {
848 test := NewAuthOnlyEndpointTest()
849 startSession := &sessions.SessionState{
850 Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: time.Now()}
851 test.SaveSession(startSession)
852 test.validateUser = false
854 test.proxy.ServeHTTP(test.rw, test.req)
855 assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
856 bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
857 assert.Equal(t, "unauthorized request\n", string(bodyBytes))
860func TestAuthOnlyEndpointUnauthorizedOnProviderGroupValidationFailure(t *testing.T) {
861 test := NewAuthOnlyEndpointTest()
862 startSession := &sessions.SessionState{
863 Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: time.Now()}
864 test.SaveSession(startSession)
865 provider := &TestProvider{
866 ValidToken: true,
867 GroupValidator: func(s string) bool {
868 return false
869 },
870 }
872 test.proxy.provider = provider
873 test.proxy.ServeHTTP(test.rw, test.req)
874 assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
875 bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
876 assert.Equal(t, "unauthorized request\n", string(bodyBytes))
879func TestAuthOnlyEndpointSetXAuthRequestHeaders(t *testing.T) {
880 var pcTest ProcessCookieTest
882 pcTest.opts = NewOptions()
883 pcTest.opts.SetXAuthRequest = true
884 pcTest.opts.Validate()
886 pcTest.proxy = NewOAuthProxy(pcTest.opts, func(email string) bool {
887 return pcTest.validateUser
888 })
889 pcTest.proxy.provider = &TestProvider{
890 ValidToken: true,
891 }
893 pcTest.validateUser = true
895 pcTest.rw = httptest.NewRecorder()
896 pcTest.req, _ = http.NewRequest("GET",
897 pcTest.opts.ProxyPrefix+"/auth", nil)
899 startSession := &sessions.SessionState{
900 User: "oauth_user", Email: "oauth_user@example.com", AccessToken: "oauth_token", CreatedAt: time.Now()}
901 pcTest.SaveSession(startSession)
903 pcTest.proxy.ServeHTTP(pcTest.rw, pcTest.req)
904 assert.Equal(t, http.StatusAccepted, pcTest.rw.Code)
905 assert.Equal(t, "oauth_user", pcTest.rw.HeaderMap["X-Auth-Request-User"][0])
906 assert.Equal(t, "oauth_user@example.com", pcTest.rw.HeaderMap["X-Auth-Request-Email"][0])
909func TestAuthSkippedForPreflightRequests(t *testing.T) {
910 upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
911 w.WriteHeader(200)
912 w.Write([]byte("response"))
913 }))
914 defer upstream.Close()
916 opts := NewOptions()
917 opts.Upstreams = append(opts.Upstreams, upstream.URL)
918 opts.ClientID = "aljsal"
919 opts.ClientSecret = "jglkfsdgj"
920 opts.CookieSecret = "dkfjgdls"
921 opts.SkipAuthPreflight = true
922 opts.Validate()
924 upstreamURL, _ := url.Parse(upstream.URL)
925 opts.provider = NewTestProvider(upstreamURL, "")
927 proxy := NewOAuthProxy(opts, func(string) bool { return false })
928 rw := httptest.NewRecorder()
929 req, _ := http.NewRequest("OPTIONS", "/preflight-request", nil)
930 proxy.ServeHTTP(rw, req)
932 assert.Equal(t, 200, rw.Code)
933 assert.Equal(t, "response", rw.Body.String())
936type SignatureAuthenticator struct {
937 auth hmacauth.HmacAuth
940func (v *SignatureAuthenticator) Authenticate(w http.ResponseWriter, r *http.Request) {
941 result, headerSig, computedSig := v.auth.AuthenticateRequest(r)
942 if result == hmacauth.ResultNoSignature {
943 w.Write([]byte("no signature received"))
944 } else if result == hmacauth.ResultMatch {
945 w.Write([]byte("signatures match"))
946 } else if result == hmacauth.ResultMismatch {
947 w.Write([]byte("signatures do not match:" +
948 "\n received: " + headerSig +
949 "\n computed: " + computedSig))
950 } else {
951 panic("Unknown result value: " + result.String())
952 }
955type SignatureTest struct {
956 opts *Options
957 upstream *httptest.Server
958 upstreamHost string
959 provider *httptest.Server
960 header http.Header
961 rw *httptest.ResponseRecorder
962 authenticator *SignatureAuthenticator
965func NewSignatureTest() *SignatureTest {
966 opts := NewOptions()
967 opts.CookieSecret = "cookie secret"
968 opts.ClientID = "client ID"
969 opts.ClientSecret = "client secret"
970 opts.EmailDomains = []string{"acm.org"}
972 authenticator := &SignatureAuthenticator{}
973 upstream := httptest.NewServer(
974 http.HandlerFunc(authenticator.Authenticate))
975 upstreamURL, _ := url.Parse(upstream.URL)
976 opts.Upstreams = append(opts.Upstreams, upstream.URL)
978 providerHandler := func(w http.ResponseWriter, r *http.Request) {
979 w.Write([]byte(`{"access_token": "my_auth_token"}`))
980 }
981 provider := httptest.NewServer(http.HandlerFunc(providerHandler))
982 providerURL, _ := url.Parse(provider.URL)
983 opts.provider = NewTestProvider(providerURL, "mbland@acm.org")
985 return &SignatureTest{
986 opts,
987 upstream,
988 upstreamURL.Host,
989 provider,
990 make(http.Header),
991 httptest.NewRecorder(),
992 authenticator,
993 }
996func (st *SignatureTest) Close() {
997 st.provider.Close()
998 st.upstream.Close()
1001// fakeNetConn simulates an http.Request.Body buffer that will be consumed
1002// when it is read by the hmacauth.HmacAuth if not handled properly. See:
1003// https://github.com/18F/hmacauth/pull/4
1004type fakeNetConn struct {
1005 reqBody string
1008func (fnc *fakeNetConn) Read(p []byte) (n int, err error) {
1009 if bodyLen := len(fnc.reqBody); bodyLen != 0 {
1010 copy(p, fnc.reqBody)
1011 fnc.reqBody = ""
1012 return bodyLen, io.EOF
1014 return 0, io.EOF
1017func (st *SignatureTest) MakeRequestWithExpectedKey(method, body, key string) {
1018 err := st.opts.Validate()
1019 if err != nil {
1020 panic(err)
1022 proxy := NewOAuthProxy(st.opts, func(email string) bool { return true })
1024 var bodyBuf io.ReadCloser
1025 if body != "" {
1026 bodyBuf = ioutil.NopCloser(&fakeNetConn{reqBody: body})
1028 req := httptest.NewRequest(method, "/foo/bar", bodyBuf)
1029 req.Header = st.header
1031 state := &sessions.SessionState{
1032 Email: "mbland@acm.org", AccessToken: "my_access_token"}
1033 err = proxy.SaveSession(st.rw, req, state)
1034 if err != nil {
1035 panic(err)
1037 for _, c := range st.rw.Result().Cookies() {
1038 req.AddCookie(c)
1040 // This is used by the upstream to validate the signature.
1041 st.authenticator.auth = hmacauth.NewHmacAuth(
1042 crypto.SHA1, []byte(key), SignatureHeader, SignatureHeaders)
1043 proxy.ServeHTTP(st.rw, req)
1046func TestNoRequestSignature(t *testing.T) {
1047 st := NewSignatureTest()
1048 defer st.Close()
1049 st.MakeRequestWithExpectedKey("GET", "", "")
1050 assert.Equal(t, 200, st.rw.Code)
1051 assert.Equal(t, st.rw.Body.String(), "no signature received")
1054func TestRequestSignatureGetRequest(t *testing.T) {
1055 st := NewSignatureTest()
1056 defer st.Close()
1057 st.opts.SignatureKey = "sha1:7d9e1aa87a5954e6f9fc59266b3af9d7c35fda2d"
1058 st.MakeRequestWithExpectedKey("GET", "", "7d9e1aa87a5954e6f9fc59266b3af9d7c35fda2d")
1059 assert.Equal(t, 200, st.rw.Code)
1060 assert.Equal(t, st.rw.Body.String(), "signatures match")
1063func TestRequestSignaturePostRequest(t *testing.T) {
1064 st := NewSignatureTest()
1065 defer st.Close()
1066 st.opts.SignatureKey = "sha1:d90df39e2d19282840252612dd7c81421a372f61"
1067 payload := `{ "hello": "world!" }`
1068 st.MakeRequestWithExpectedKey("POST", payload, "d90df39e2d19282840252612dd7c81421a372f61")
1069 assert.Equal(t, 200, st.rw.Code)
1070 assert.Equal(t, st.rw.Body.String(), "signatures match")
1073func TestGetRedirect(t *testing.T) {
1074 options := NewOptions()
1075 _ = options.Validate()
1076 require.NotEmpty(t, options.ProxyPrefix)
1077 proxy := NewOAuthProxy(options, func(s string) bool { return false })
1079 tests := []struct {
1080 name string
1081 url string
1082 expectedRedirect string
1083 }{
1085 name: "request outside of ProxyPrefix redirects to original URL",
1086 url: "/foo/bar",
1087 expectedRedirect: "/foo/bar",
1088 },
1090 name: "request under ProxyPrefix redirects to root",
1091 url: proxy.ProxyPrefix + "/foo/bar",
1092 expectedRedirect: "/",
1093 },
1095 for _, tt := range tests {
1096 t.Run(tt.name, func(t *testing.T) {
1097 req, _ := http.NewRequest("GET", tt.url, nil)
1098 redirect, err := proxy.GetRedirect(req)
1100 assert.NoError(t, err)
1101 assert.Equal(t, tt.expectedRedirect, redirect)
1102 })
1106type ajaxRequestTest struct {
1107 opts *Options
1108 proxy *OAuthProxy
1111func newAjaxRequestTest() *ajaxRequestTest {
1112 test := &ajaxRequestTest{}
1113 test.opts = NewOptions()
1114 test.opts.CookieSecret = "sdflsw"
1115 test.opts.ClientID = "gkljfdl"
1116 test.opts.ClientSecret = "sdflkjs"
1117 test.opts.Validate()
1118 test.proxy = NewOAuthProxy(test.opts, func(email string) bool {
1119 return true
1120 })
1121 return test
1124func (test *ajaxRequestTest) getEndpoint(endpoint string, header http.Header) (int, http.Header, error) {
1125 rw := httptest.NewRecorder()
1126 req, err := http.NewRequest(http.MethodGet, endpoint, strings.NewReader(""))
1127 if err != nil {
1128 return 0, nil, err
1130 req.Header = header
1131 test.proxy.ServeHTTP(rw, req)
1132 return rw.Code, rw.Header(), nil
1135func testAjaxUnauthorizedRequest(t *testing.T, header http.Header) {
1136 test := newAjaxRequestTest()
1137 endpoint := "/test"
1139 code, rh, err := test.getEndpoint(endpoint, header)
1140 assert.NoError(t, err)
1141 assert.Equal(t, http.StatusUnauthorized, code)
1142 mime := rh.Get("Content-Type")
1143 assert.Equal(t, applicationJSON, mime)
1145func TestAjaxUnauthorizedRequest1(t *testing.T) {
1146 header := make(http.Header)
1147 header.Add("accept", applicationJSON)
1149 testAjaxUnauthorizedRequest(t, header)
1152func TestAjaxUnauthorizedRequest2(t *testing.T) {
1153 header := make(http.Header)
1154 header.Add("Accept", applicationJSON)
1156 testAjaxUnauthorizedRequest(t, header)
1159func TestAjaxForbiddendRequest(t *testing.T) {
1160 test := newAjaxRequestTest()
1161 endpoint := "/test"
1162 header := make(http.Header)
1163 code, rh, err := test.getEndpoint(endpoint, header)
1164 assert.NoError(t, err)
1165 assert.Equal(t, http.StatusForbidden, code)
1166 mime := rh.Get("Content-Type")
1167 assert.NotEqual(t, applicationJSON, mime)
1170func TestClearSplitCookie(t *testing.T) {
1171 opts := NewOptions()
1172 opts.CookieName = "oauth2"
1173 opts.CookieDomain = "abc"
1174 store, err := cookie.NewCookieSessionStore(&opts.SessionOptions, &opts.CookieOptions)
1175 assert.Equal(t, err, nil)
1176 p := OAuthProxy{CookieName: opts.CookieName, CookieDomain: opts.CookieDomain, sessionStore: store}
1177 var rw = httptest.NewRecorder()
1178 req := httptest.NewRequest("get", "/", nil)
1180 req.AddCookie(&http.Cookie{
1181 Name: "test1",
1182 Value: "test1",
1183 })
1184 req.AddCookie(&http.Cookie{
1185 Name: "oauth2_0",
1186 Value: "oauth2_0",
1187 })
1188 req.AddCookie(&http.Cookie{
1189 Name: "oauth2_1",
1190 Value: "oauth2_1",
1191 })
1193 p.ClearSessionCookie(rw, req)
1194 header := rw.Header()
1196 assert.Equal(t, 2, len(header["Set-Cookie"]), "should have 3 set-cookie header entries")
1199func TestClearSingleCookie(t *testing.T) {
1200 opts := NewOptions()
1201 opts.CookieName = "oauth2"
1202 opts.CookieDomain = "abc"
1203 store, err := cookie.NewCookieSessionStore(&opts.SessionOptions, &opts.CookieOptions)
1204 assert.Equal(t, err, nil)
1205 p := OAuthProxy{CookieName: opts.CookieName, CookieDomain: opts.CookieDomain, sessionStore: store}
1206 var rw = httptest.NewRecorder()
1207 req := httptest.NewRequest("get", "/", nil)
1209 req.AddCookie(&http.Cookie{
1210 Name: "test1",
1211 Value: "test1",
1212 })
1213 req.AddCookie(&http.Cookie{
1214 Name: "oauth2",
1215 Value: "oauth2",
1216 })
1218 p.ClearSessionCookie(rw, req)
1219 header := rw.Header()
1221 assert.Equal(t, 1, len(header["Set-Cookie"]), "should have 1 set-cookie header entries")
1224type NoOpKeySet struct {
1227func (NoOpKeySet) VerifySignature(ctx context.Context, jwt string) (payload []byte, err error) {
1228 splitStrings := strings.Split(jwt, ".")
1229 payloadString := splitStrings[1]
1230 jsonString, err := base64.RawURLEncoding.DecodeString(payloadString)
1231 return []byte(jsonString), err
1234func TestGetJwtSession(t *testing.T) {
1235 /* token payload:
1237 "sub": "1234567890",
1238 "aud": "https://test.myapp.com",
1239 "name": "John Doe",
1240 "email": "john@example.com",
1241 "iss": "https://issuer.example.com",
1242 "iat": 1553691215,
1243 "exp": 1912151821
1245 */
1246 goodJwt := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9." +
1247 "eyJzdWIiOiIxMjM0NTY3ODkwIiwiYXVkIjoiaHR0cHM6Ly90ZXN0Lm15YXBwLmNvbSIsIm5hbWUiOiJKb2huIERvZSIsImVtY" +
1248 "WlsIjoiam9obkBleGFtcGxlLmNvbSIsImlzcyI6Imh0dHBzOi8vaXNzdWVyLmV4YW1wbGUuY29tIiwiaWF0IjoxNTUzNjkxMj" +
1249 "E1LCJleHAiOjE5MTIxNTE4MjF9." +
1250 "rLVyzOnEldUq_pNkfa-WiV8TVJYWyZCaM2Am_uo8FGg11zD7l-qmz3x1seTvqpH6Y0Ty00fmv6dJnGnC8WMnPXQiodRTfhBSe" +
1251 "OKZMu0HkMD2sg52zlKkbfLTO6ic5VnbVgwjjrB8am_Ta6w7kyFUaB5C1BsIrrLMldkWEhynbb8"
1253 keyset := NoOpKeySet{}
1254 verifier := oidc.NewVerifier("https://issuer.example.com", keyset,
1255 &oidc.Config{ClientID: "https://test.myapp.com", SkipExpiryCheck: true})
1257 test := NewAuthOnlyEndpointTest(func(opts *Options) {
1258 opts.PassAuthorization = true
1259 opts.SetAuthorization = true
1260 opts.SetXAuthRequest = true
1261 opts.SkipJwtBearerTokens = true
1262 opts.jwtBearerVerifiers = append(opts.jwtBearerVerifiers, verifier)
1263 })
1264 tp, _ := test.proxy.provider.(*TestProvider)
1265 tp.GroupValidator = func(s string) bool {
1266 return true
1269 authHeader := fmt.Sprintf("Bearer %s", goodJwt)
1270 test.req.Header = map[string][]string{
1271 "Authorization": {authHeader},
1274 // Bearer
1275 session, _ := test.proxy.GetJwtSession(test.req)
1276 assert.Equal(t, session.User, "john@example.com")
1277 assert.Equal(t, session.Email, "john@example.com")
1278 assert.Equal(t, session.ExpiresOn, time.Unix(1912151821, 0))
1279 assert.Equal(t, session.IDToken, goodJwt)
1281 test.proxy.ServeHTTP(test.rw, test.req)
1282 if test.rw.Code >= 400 {
1283 t.Fatalf("expected 3xx got %d", test.rw.Code)
1286 // Check PassAuthorization, should overwrite Basic header
1287 assert.Equal(t, test.req.Header.Get("Authorization"), authHeader)
1288 assert.Equal(t, test.req.Header.Get("X-Forwarded-User"), "john@example.com")
1289 assert.Equal(t, test.req.Header.Get("X-Forwarded-Email"), "john@example.com")
1291 // SetAuthorization and SetXAuthRequest
1292 assert.Equal(t, test.rw.Header().Get("Authorization"), authHeader)
1293 assert.Equal(t, test.rw.Header().Get("X-Auth-Request-User"), "john@example.com")
1294 assert.Equal(t, test.rw.Header().Get("X-Auth-Request-Email"), "john@example.com")
1297func TestJwtUnauthorizedOnGroupValidationFailure(t *testing.T) {
1298 goodJwt := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9." +
1299 "eyJzdWIiOiIxMjM0NTY3ODkwIiwiYXVkIjoiaHR0cHM6Ly90ZXN0Lm15YXBwLmNvbSIsIm5hbWUiOiJKb2huIERvZSIsImVtY" +
1300 "WlsIjoiam9obkBleGFtcGxlLmNvbSIsImlzcyI6Imh0dHBzOi8vaXNzdWVyLmV4YW1wbGUuY29tIiwiaWF0IjoxNTUzNjkxMj" +
1301 "E1LCJleHAiOjE5MTIxNTE4MjF9." +
1302 "rLVyzOnEldUq_pNkfa-WiV8TVJYWyZCaM2Am_uo8FGg11zD7l-qmz3x1seTvqpH6Y0Ty00fmv6dJnGnC8WMnPXQiodRTfhBSe" +
1303 "OKZMu0HkMD2sg52zlKkbfLTO6ic5VnbVgwjjrB8am_Ta6w7kyFUaB5C1BsIrrLMldkWEhynbb8"
1305 keyset := NoOpKeySet{}
1306 verifier := oidc.NewVerifier("https://issuer.example.com", keyset,
1307 &oidc.Config{ClientID: "https://test.myapp.com", SkipExpiryCheck: true})
1309 test := NewAuthOnlyEndpointTest(func(opts *Options) {
1310 opts.PassAuthorization = true
1311 opts.SetAuthorization = true
1312 opts.SetXAuthRequest = true
1313 opts.SkipJwtBearerTokens = true
1314 opts.jwtBearerVerifiers = append(opts.jwtBearerVerifiers, verifier)
1315 })
1316 tp, _ := test.proxy.provider.(*TestProvider)
1317 // Verify ValidateGroup fails JWT authorization
1318 tp.GroupValidator = func(s string) bool {
1319 return false
1322 authHeader := fmt.Sprintf("Bearer %s", goodJwt)
1323 test.req.Header = map[string][]string{
1324 "Authorization": {authHeader},
1326 test.proxy.ServeHTTP(test.rw, test.req)
1327 if test.rw.Code != http.StatusUnauthorized {
1328 t.Fatalf("expected 401 got %d", test.rw.Code)
1332func TestFindJwtBearerToken(t *testing.T) {
1333 p := OAuthProxy{CookieName: "oauth2", CookieDomain: "abc"}
1334 getReq := &http.Request{URL: &url.URL{Scheme: "http", Host: "example.com"}}
1336 validToken := "eyJfoobar.eyJfoobar.12345asdf"
1337 var token string
1339 // Bearer
1340 getReq.Header = map[string][]string{
1341 "Authorization": {fmt.Sprintf("Bearer %s", validToken)},
1344 token, _ = p.findBearerToken(getReq)
1345 assert.Equal(t, validToken, token)
1347 // Basic - no password
1348 getReq.SetBasicAuth(token, "")
1349 token, _ = p.findBearerToken(getReq)
1350 assert.Equal(t, validToken, token)
1352 // Basic - sentinel password
1353 getReq.SetBasicAuth(token, "x-oauth-basic")
1354 token, _ = p.findBearerToken(getReq)
1355 assert.Equal(t, validToken, token)
1357 // Basic - any username, password matching jwt pattern
1358 getReq.SetBasicAuth("any-username-you-could-wish-for", token)
1359 token, _ = p.findBearerToken(getReq)
1360 assert.Equal(t, validToken, token)
1362 failures := []string{
1363 // Too many parts
1364 "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
1365 // Not enough parts
1366 "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA",
1367 // Invalid encrypted key
1368 "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.//////.dGVzdA.dGVzdA.dGVzdA",
1369 // Invalid IV
1370 "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.//////.dGVzdA.dGVzdA",
1371 // Invalid ciphertext
1372 "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.//////.dGVzdA",
1373 // Invalid tag
1374 "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA.//////",
1375 // Invalid header
1376 "W10.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
1377 // Invalid header
1378 "######.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
1379 // Missing alc/enc params
1380 "e30.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
1383 for _, failure := range failures {
1384 getReq.Header = map[string][]string{
1385 "Authorization": {fmt.Sprintf("Bearer %s", failure)},
1387 _, err := p.findBearerToken(getReq)
1388 assert.Error(t, err)
1391 fmt.Printf("%s", token)