git.iko.soy/typeable/oauth2-proxy
on master help sign in
Prevent browser caching during auth flow (#453)

* Prevent browser caching during auth flow * simplify no-cache logic, add tests and update changelog * checking noCacheHeaders does not exists in response headers from upstream * remove unnecessary codes * add no-cache headers in SignInPage and OAuthStart for proxy mode https://github.com/oauth2-proxy/oauth2-proxy/pull/453#discussion_r405072222

commit7efc162aaa1a8cbd540fe1d2dab2106ddd8278d4 authorMitsuo Heijo <mitsuo.heijo@gmail.com> dateThu 9 Apr 2020, 23:39 (7 years ago) parent7c3efe4
3 files changed+67
CHANGELOG.md+3 oauthproxy.go+21 oauthproxy_test.go+43
CHANGELOG.md+3
@@ -4,6 +4,8 @@
44
55## Important Notes
66
7+- [#453](https://github.com/oauth2-proxy/oauth2-proxy/pull/453) Responses to endpoints with a proxy prefix will now return headers for preventing browser caching.
8+
79## Breaking Changes
810
911- Migration from Pusher to independent org may have introduced breaking changes for your environment.
@@ -12,6 +14,7 @@
1214
1315## Changes since v5.1.0
1416
17+- [#453](https://github.com/oauth2-proxy/oauth2-proxy/pull/453) Prevent browser caching during auth flow (@johejo)
1518- [#481](https://github.com/oauth2-proxy/oauth2-proxy/pull/481) Update Okta docs (@trevorbox)
1619- [#474](https://github.com/oauth2-proxy/oauth2-proxy/pull/474) Always log hasMember request error object (@jbielick)
1720- [#468](https://github.com/oauth2-proxy/oauth2-proxy/pull/468) Implement graceful shutdown and propagate request context (@johejo)
oauthproxy.go+21
@@ -453,6 +453,7 @@ func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, code int, title string, m
453453
454454// SignInPage writes the sing in template to the response
455455func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code int) {
456+ prepareNoCache(rw)
456457 p.ClearSessionCookie(rw, req)
457458 rw.WriteHeader(code)
458459
@@ -633,7 +634,26 @@ func getRemoteAddr(req *http.Request) (s string) {
633634 return
634635}
635636
637+// See https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=en
638+var noCacheHeaders = map[string]string{
639+ "Expires": time.Unix(0, 0).Format(time.RFC1123),
640+ "Cache-Control": "no-cache, no-store, must-revalidate, max-age=0",
641+ "X-Accel-Expires": "0", // https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/
642+}
643+
644+// prepareNoCache prepares headers for preventing browser caching.
645+func prepareNoCache(w http.ResponseWriter) {
646+ // Set NoCache headers
647+ for k, v := range noCacheHeaders {
648+ w.Header().Set(k, v)
649+ }
650+}
651+
636652func (p *OAuthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
653+ if strings.HasPrefix(req.URL.Path, p.ProxyPrefix) {
654+ prepareNoCache(rw)
655+ }
656+
637657 switch path := req.URL.Path; {
638658 case path == p.RobotsPath:
639659 p.RobotsTxt(rw)
@@ -715,6 +735,7 @@ func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) {
715735
716736// OAuthStart starts the OAuth2 authentication flow
717737func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) {
738+ prepareNoCache(rw)
718739 nonce, err := encryption.Nonce()
719740 if err != nil {
720741 logger.Printf("Error obtaining nonce: %s", err.Error())
oauthproxy_test.go+43
@@ -1524,3 +1524,46 @@ func TestFindJwtBearerToken(t *testing.T) {
15241524
15251525 fmt.Printf("%s", token)
15261526}
1527+
1528+func Test_prepareNoCache(t *testing.T) {
1529+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1530+ prepareNoCache(w)
1531+ })
1532+ mux := http.NewServeMux()
1533+ mux.Handle("/", handler)
1534+
1535+ rec := httptest.NewRecorder()
1536+ req := httptest.NewRequest(http.MethodGet, "/", nil)
1537+ mux.ServeHTTP(rec, req)
1538+
1539+ for k, v := range noCacheHeaders {
1540+ assert.Equal(t, rec.Header().Get(k), v)
1541+ }
1542+}
1543+
1544+func Test_noCacheHeadersDoesNotExistsInResponseHeadersFromUpstream(t *testing.T) {
1545+ upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1546+ w.Write([]byte("upstream"))
1547+ }))
1548+ t.Cleanup(upstream.Close)
1549+
1550+ opts := NewOptions()
1551+ opts.Upstreams = []string{upstream.URL}
1552+ opts.SkipAuthRegex = []string{".*"}
1553+ _ = opts.Validate()
1554+ proxy := NewOAuthProxy(opts, func(email string) bool {
1555+ return true
1556+ })
1557+
1558+ rec := httptest.NewRecorder()
1559+ req := httptest.NewRequest(http.MethodGet, "/upstream", nil)
1560+ proxy.ServeHTTP(rec, req)
1561+
1562+ assert.Equal(t, http.StatusOK, rec.Code)
1563+ assert.Equal(t, "upstream", rec.Body.String())
1564+
1565+ // checking noCacheHeaders does not exists in response headers from upstream
1566+ for k := range noCacheHeaders {
1567+ assert.Equal(t, "", rec.Header().Get(k))
1568+ }
1569+}