diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ed07159..8ff23ee3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - [#432](https://github.com/oauth2-proxy/oauth2-proxy/pull/432) Update ruby dependencies for documentation (@theobarberbany) - [#471](https://github.com/oauth2-proxy/oauth2-proxy/pull/471) Add logging in case of invalid redirects (@gargath) - [#462](https://github.com/oauth2-proxy/oauth2-proxy/pull/462) Allow HTML in banner message (@eritikass). +- [#413](https://github.com/oauth2-proxy/oauth2-proxy/pull/413) Add -set-basic-auth param to set the Basic Authorization header for upstreams (@morarucostel). # v5.1.0 diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md index 6b61dc24..cb2db72c 100644 --- a/docs/configuration/configuration.md +++ b/docs/configuration/configuration.md @@ -103,6 +103,7 @@ An example [oauth2-proxy.cfg]({{ site.gitweb }}/contrib/oauth2-proxy.cfg.example | `-session-store-type` | string | [Session data storage backend](configuration/sessions); redis or cookie | cookie | | `-set-xauthrequest` | bool | set X-Auth-Request-User, X-Auth-Request-Email and X-Auth-Request-Preferred-Username response headers (useful in Nginx auth_request mode) | false | | `-set-authorization-header` | bool | set Authorization Bearer response header (useful in Nginx auth_request mode) | false | +| `-set-basic-auth` | bool | set HTTP Basic Auth information in response (useful in Nginx auth_request mode) | true | | `-signature-key` | string | GAP-Signature request signature key (algorithm:secretkey) | | | `-silence-ping-logging` | bool | disable logging of requests to ping endpoint | false | | `-skip-auth-preflight` | bool | will skip authentication for OPTIONS requests | false | diff --git a/main.go b/main.go index 1af017c5..bcd6bec5 100644 --- a/main.go +++ b/main.go @@ -43,6 +43,7 @@ func main() { flagSet.Bool("set-xauthrequest", false, "set X-Auth-Request-User and X-Auth-Request-Email response headers (useful in Nginx auth_request mode)") flagSet.Var(&upstreams, "upstream", "the http url(s) of the upstream endpoint, file:// paths for static files or static:// for static response. Routing is based on the path") flagSet.Bool("pass-basic-auth", true, "pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream") + flagSet.Bool("set-basic-auth", true, "set HTTP Basic Auth information in response (useful in Nginx auth_request mode)") flagSet.Bool("prefer-email-to-user", false, "Prefer to use the Email address as the Username when passing information to upstream. Will only use Username if Email is unavailable, eg. htaccess authentication. Used in conjunction with -pass-basic-auth and -pass-user-headers") flagSet.Bool("pass-user-headers", true, "pass X-Forwarded-User and X-Forwarded-Email information to upstream") flagSet.String("basic-auth-password", "", "the password to set when passing the HTTP Basic Auth header") diff --git a/oauthproxy.go b/oauthproxy.go index c5470544..dbe379a4 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -94,6 +94,7 @@ type OAuthProxy struct { serveMux http.Handler SetXAuthRequest bool PassBasicAuth bool + SetBasicAuth bool SkipProviderButton bool PassUserHeaders bool BasicAuthPassword string @@ -302,6 +303,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { compiledRegex: opts.CompiledRegex, SetXAuthRequest: opts.SetXAuthRequest, PassBasicAuth: opts.PassBasicAuth, + SetBasicAuth: opts.SetBasicAuth, PassUserHeaders: opts.PassUserHeaders, BasicAuthPassword: opts.BasicAuthPassword, PassAccessToken: opts.PassAccessToken, @@ -1037,6 +1039,14 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req req.Header.Del("Authorization") } } + if p.SetBasicAuth { + if session.User != "" { + authVal := b64.StdEncoding.EncodeToString([]byte(session.User + ":" + p.BasicAuthPassword)) + rw.Header().Set("Authorization", "Basic "+authVal) + } else { + rw.Header().Del("Authorization") + } + } if p.SetAuthorization { if session.IDToken != "" { rw.Header().Set("Authorization", fmt.Sprintf("Bearer %s", session.IDToken)) diff --git a/oauthproxy_test.go b/oauthproxy_test.go index ec46bdd8..86cfc90a 100644 --- a/oauthproxy_test.go +++ b/oauthproxy_test.go @@ -404,6 +404,7 @@ func TestBasicAuthPassword(t *testing.T) { opts.ClientSecret = "alkgret" opts.CookieSecure = false opts.PassBasicAuth = true + opts.SetBasicAuth = true opts.PassUserHeaders = true opts.PreferEmailToUser = true opts.BasicAuthPassword = "This is a secure password" @@ -1075,6 +1076,71 @@ func TestAuthOnlyEndpointSetXAuthRequestHeaders(t *testing.T) { assert.Equal(t, "oauth_user@example.com", pcTest.rw.HeaderMap["X-Auth-Request-Email"][0]) } +func TestAuthOnlyEndpointSetBasicAuthTrueRequestHeaders(t *testing.T) { + var pcTest ProcessCookieTest + + pcTest.opts = NewOptions() + pcTest.opts.SetXAuthRequest = true + pcTest.opts.SetBasicAuth = true + pcTest.opts.Validate() + + pcTest.proxy = NewOAuthProxy(pcTest.opts, func(email string) bool { + return pcTest.validateUser + }) + pcTest.proxy.provider = &TestProvider{ + ValidToken: true, + } + + pcTest.validateUser = true + + pcTest.rw = httptest.NewRecorder() + pcTest.req, _ = http.NewRequest("GET", + pcTest.opts.ProxyPrefix+"/auth", nil) + + startSession := &sessions.SessionState{ + User: "oauth_user", Email: "oauth_user@example.com", AccessToken: "oauth_token", CreatedAt: time.Now()} + pcTest.SaveSession(startSession) + + pcTest.proxy.ServeHTTP(pcTest.rw, pcTest.req) + assert.Equal(t, http.StatusAccepted, pcTest.rw.Code) + assert.Equal(t, "oauth_user", pcTest.rw.HeaderMap["X-Auth-Request-User"][0]) + assert.Equal(t, "oauth_user@example.com", pcTest.rw.HeaderMap["X-Auth-Request-Email"][0]) + expectedHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte("oauth_user:"+pcTest.opts.BasicAuthPassword)) + assert.Equal(t, expectedHeader, pcTest.rw.HeaderMap["Authorization"][0]) +} + +func TestAuthOnlyEndpointSetBasicAuthFalseRequestHeaders(t *testing.T) { + var pcTest ProcessCookieTest + + pcTest.opts = NewOptions() + pcTest.opts.SetXAuthRequest = true + pcTest.opts.SetBasicAuth = false + pcTest.opts.Validate() + + pcTest.proxy = NewOAuthProxy(pcTest.opts, func(email string) bool { + return pcTest.validateUser + }) + pcTest.proxy.provider = &TestProvider{ + ValidToken: true, + } + + pcTest.validateUser = true + + pcTest.rw = httptest.NewRecorder() + pcTest.req, _ = http.NewRequest("GET", + pcTest.opts.ProxyPrefix+"/auth", nil) + + startSession := &sessions.SessionState{ + User: "oauth_user", Email: "oauth_user@example.com", AccessToken: "oauth_token", CreatedAt: time.Now()} + pcTest.SaveSession(startSession) + + pcTest.proxy.ServeHTTP(pcTest.rw, pcTest.req) + assert.Equal(t, http.StatusAccepted, pcTest.rw.Code) + assert.Equal(t, "oauth_user", pcTest.rw.HeaderMap["X-Auth-Request-User"][0]) + assert.Equal(t, "oauth_user@example.com", pcTest.rw.HeaderMap["X-Auth-Request-Email"][0]) + assert.Equal(t, 0, len(pcTest.rw.HeaderMap["Authorization"]), "should not have Authorization header entries") +} + func TestAuthSkippedForPreflightRequests(t *testing.T) { upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) diff --git a/options.go b/options.go index b5cb8590..abfc6013 100644 --- a/options.go +++ b/options.go @@ -73,6 +73,7 @@ type Options struct { SkipJwtBearerTokens bool `flag:"skip-jwt-bearer-tokens" cfg:"skip_jwt_bearer_tokens" env:"OAUTH2_PROXY_SKIP_JWT_BEARER_TOKENS"` ExtraJwtIssuers []string `flag:"extra-jwt-issuers" cfg:"extra_jwt_issuers" env:"OAUTH2_PROXY_EXTRA_JWT_ISSUERS"` PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth" env:"OAUTH2_PROXY_PASS_BASIC_AUTH"` + SetBasicAuth bool `flag:"set-basic-auth" cfg:"set_basic_auth" env:"OAUTH2_PROXY_SET_BASIC_AUTH"` PreferEmailToUser bool `flag:"prefer-email-to-user" cfg:"prefer_email_to_user" env:"OAUTH2_PROXY_PREFER_EMAIL_TO_USER"` BasicAuthPassword string `flag:"basic-auth-password" cfg:"basic_auth_password" env:"OAUTH2_PROXY_BASIC_AUTH_PASSWORD"` PassAccessToken bool `flag:"pass-access-token" cfg:"pass_access_token" env:"OAUTH2_PROXY_PASS_ACCESS_TOKEN"` @@ -166,6 +167,7 @@ func NewOptions() *Options { SetXAuthRequest: false, SkipAuthPreflight: false, PassBasicAuth: true, + SetBasicAuth: false, PassUserHeaders: true, PassAccessToken: false, PassHostHeader: true, @@ -243,6 +245,10 @@ func (o *Options) Validate() error { "\n use email-domain=* to authorize all email addresses") } + if o.SetBasicAuth && o.SetAuthorization { + msgs = append(msgs, "mutually exclusive: set-basic-auth and set-authorization-header can not both be true") + } + if o.OIDCIssuerURL != "" { ctx := context.Background()