From 1e7d2a08a3df581ffbbcf807f3add6a3f129f36e Mon Sep 17 00:00:00 2001 From: idntfy Date: Fri, 7 Apr 2017 14:55:48 +0300 Subject: [PATCH] #369: Optionally allow skipping authentication for preflight requests --- README.md | 1 + main.go | 1 + oauthproxy.go | 9 ++++++++- oauthproxy_test.go | 27 +++++++++++++++++++++++++++ options.go | 2 ++ 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9333a66..68a8138 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,7 @@ Usage of oauth2_proxy: -scope="": Oauth scope specification -signature-key="": GAP-Signature request signature key (algorithm:secretkey) -skip-auth-regex=: bypass authentication for requests path's that match (may be given multiple times) + -skip-auth-preflight=false: bypass authentication for OPTIONAL requests so preflight requests could succeed when using CORS -skip-provider-button=false: will skip sign-in-page to directly reach the next step: oauth/start -ssl-insecure-skip-verify: skip validation of certificates presented when using HTTPS -tls-cert="": path to certificate file diff --git a/main.go b/main.go index b5ed971..ab0e4d3 100644 --- a/main.go +++ b/main.go @@ -39,6 +39,7 @@ func main() { flagSet.Bool("pass-host-header", true, "pass the request Host Header to upstream") flagSet.Var(&skipAuthRegex, "skip-auth-regex", "bypass authentication for requests path's that match (may be given multiple times)") flagSet.Bool("skip-provider-button", false, "will skip sign-in-page to directly reach the next step: oauth/start") + flagSet.Bool("skip-auth-preflight", false, "will skip authentication for OPTIONS requests") flagSet.Bool("ssl-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS") flagSet.Var(&emailDomains, "email-domain", "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email") diff --git a/oauthproxy.go b/oauthproxy.go index f4cd577..dd2b58e 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -68,6 +68,7 @@ type OAuthProxy struct { PassAccessToken bool CookieCipher *cookie.Cipher skipAuthRegex []string + skipAuthPreflight bool compiledRegex []*regexp.Regexp templates *template.Template Footer string @@ -198,6 +199,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { serveMux: serveMux, redirectURL: redirectURL, skipAuthRegex: opts.SkipAuthRegex, + skipAuthPreflight: opts.SkipAuthPreflight, compiledRegex: opts.CompiledRegex, SetXAuthRequest: opts.SetXAuthRequest, PassBasicAuth: opts.PassBasicAuth, @@ -421,6 +423,11 @@ func (p *OAuthProxy) GetRedirect(req *http.Request) (redirect string, err error) return } +func (p *OAuthProxy) IsWhitelistedRequest(req *http.Request) (ok bool) { + isPreflightRequestAllowed := p.skipAuthPreflight && req.Method == "OPTIONS" + return isPreflightRequestAllowed || p.IsWhitelistedPath(req.URL.Path) +} + func (p *OAuthProxy) IsWhitelistedPath(path string) (ok bool) { for _, u := range p.compiledRegex { ok = u.MatchString(path) @@ -445,7 +452,7 @@ func (p *OAuthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { p.RobotsTxt(rw) case path == p.PingPath: p.PingPage(rw) - case p.IsWhitelistedPath(path): + case p.IsWhitelistedRequest(req): p.serveMux.ServeHTTP(rw, req) case path == p.SignInPath: p.SignIn(rw, req) diff --git a/oauthproxy_test.go b/oauthproxy_test.go index 0689424..a0bcc5c 100644 --- a/oauthproxy_test.go +++ b/oauthproxy_test.go @@ -641,6 +641,33 @@ func TestAuthOnlyEndpointSetXAuthRequestHeaders(t *testing.T) { assert.Equal(t, "oauth_user@example.com", pc_test.rw.HeaderMap["X-Auth-Request-Email"][0]) } +func TestAuthSkippedForPreflightRequests(t *testing.T) { + upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + w.Write([]byte("response")) + })) + defer upstream.Close() + + opts := NewOptions() + opts.Upstreams = append(opts.Upstreams, upstream.URL) + opts.ClientID = "bazquux" + opts.ClientSecret = "foobar" + opts.CookieSecret = "xyzzyplugh" + opts.SkipAuthPreflight = true + opts.Validate() + + upstream_url, _ := url.Parse(upstream.URL) + opts.provider = NewTestProvider(upstream_url, "") + + proxy := NewOAuthProxy(opts, func(string) bool { return false }) + rw := httptest.NewRecorder() + req, _ := http.NewRequest("OPTIONS", "/preflight-request", nil) + proxy.ServeHTTP(rw, req) + + assert.Equal(t, 200, rw.Code) + assert.Equal(t, "response", rw.Body.String()) +} + type SignatureAuthenticator struct { auth hmacauth.HmacAuth } diff --git a/options.go b/options.go index 63f23c6..f1df916 100644 --- a/options.go +++ b/options.go @@ -58,6 +58,7 @@ type Options struct { PassUserHeaders bool `flag:"pass-user-headers" cfg:"pass_user_headers"` SSLInsecureSkipVerify bool `flag:"ssl-insecure-skip-verify" cfg:"ssl_insecure_skip_verify"` SetXAuthRequest bool `flag:"set-xauthrequest" cfg:"set_xauthrequest"` + SkipAuthPreflight bool `flag:"skip-auth-preflight" cfg:"skip_auth_preflight"` // These options allow for other providers besides Google, with // potential overrides. @@ -99,6 +100,7 @@ func NewOptions() *Options { CookieExpire: time.Duration(168) * time.Hour, CookieRefresh: time.Duration(0), SetXAuthRequest: false, + SkipAuthPreflight: false, PassBasicAuth: true, PassUserHeaders: true, PassAccessToken: false,