Add force-json-errors flag
This commit is contained in:
		
							parent
							
								
									fd5e23e1c5
								
							
						
					
					
						commit
						d3e036d619
					
				|  | @ -21,6 +21,7 @@ | |||
| - [#1315](https://github.com/oauth2-proxy/oauth2-proxy/pull/1315) linkedin: Update provider to v2 (@wuurrd) | ||||
| - [#1348](https://github.com/oauth2-proxy/oauth2-proxy/pull/1348) Using the native httputil proxy code for websockets rather than yhat/wsutil to properly handle HTTP-level failures (@thetrime) | ||||
| - [#1379](https://github.com/oauth2-proxy/oauth2-proxy/pull/1379) Fix the manual sign in with --htpasswd-user-group switch (@janrotter) | ||||
| - [#1375](https://github.com/oauth2-proxy/oauth2-proxy/pull/1375) Added `--force-json-errors` flag (@bancek) | ||||
| - [#1337](https://github.com/oauth2-proxy/oauth2-proxy/pull/1337) Changing user field type to text when using htpasswd (@pburgisser) | ||||
| - [#1239](https://github.com/oauth2-proxy/oauth2-proxy/pull/1239) Base GitLab provider implementation on OIDCProvider (@NickMeves) | ||||
| - [#1276](https://github.com/oauth2-proxy/oauth2-proxy/pull/1276) Update crypto and switched to new github.com/golang-jwt/jwt (@JVecsei) | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ _oauth2_proxy() { | |||
| 			COMPREPLY=( $(compgen -W 'X-Real-IP X-Forwarded-For X-ProxyUser-IP' -- ${cur}) ) | ||||
| 			return 0 | ||||
| 			;; | ||||
| 		--@(http-address|https-address|redirect-url|upstream|basic-auth-password|skip-auth-regex|flush-interval|extra-jwt-issuers|email-domain|whitelist-domain|trusted-ip|keycloak-group|azure-tenant|bitbucket-team|bitbucket-repository|github-org|github-team|github-repo|github-token|gitlab-group|github-user|google-group|google-admin-email|google-service-account-json|client-id|client_secret|banner|footer|proxy-prefix|ping-path|cookie-name|cookie-secret|cookie-domain|cookie-path|cookie-expire|cookie-refresh|cookie-samesite|redist-sentinel-master-name|redist-sentinel-connection-urls|redist-cluster-connection-urls|logging-max-size|logging-max-age|logging-max-backups|standard-logging-format|request-logging-format|exclude-logging-paths|auth-logging-format|oidc-issuer-url|oidc-jwks-url|login-url|redeem-url|profile-url|resource|validate-url|scope|approval-prompt|signature-key|acr-values|jwt-key|pubjwk-url)) | ||||
| 		--@(http-address|https-address|redirect-url|upstream|basic-auth-password|skip-auth-regex|flush-interval|extra-jwt-issuers|email-domain|whitelist-domain|trusted-ip|keycloak-group|azure-tenant|bitbucket-team|bitbucket-repository|github-org|github-team|github-repo|github-token|gitlab-group|github-user|google-group|google-admin-email|google-service-account-json|client-id|client_secret|banner|footer|proxy-prefix|ping-path|cookie-name|cookie-secret|cookie-domain|cookie-path|cookie-expire|cookie-refresh|cookie-samesite|redist-sentinel-master-name|redist-sentinel-connection-urls|redist-cluster-connection-urls|logging-max-size|logging-max-age|logging-max-backups|standard-logging-format|request-logging-format|exclude-logging-paths|auth-logging-format|oidc-issuer-url|oidc-jwks-url|login-url|redeem-url|profile-url|resource|validate-url|scope|approval-prompt|signature-key|acr-values|jwt-key|pubjwk-url|force-json-errors)) | ||||
| 			return 0 | ||||
| 			;; | ||||
| 	esac | ||||
|  |  | |||
|  | @ -103,6 +103,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/ | |||
| | `--exclude-logging-paths` | string | comma separated list of paths to exclude from logging, e.g. `"/ping,/path2"` |`""` (no paths excluded) | | ||||
| | `--flush-interval` | duration | period between flushing response buffers when streaming responses | `"1s"` | | ||||
| | `--force-https` | bool | enforce https redirect | `false` | | ||||
| | `--force-json-errors` | force JSON errors instead of HTTP error pages or redirects | `false` | | ||||
| | `--banner` | string | custom (html) banner string. Use `"-"` to disable default banner. | | | ||||
| | `--footer` | string | custom (html) footer string. Use `"-"` to disable default footer. | | | ||||
| | `--github-org` | string | restrict logins to members of this organisation | | | ||||
|  |  | |||
|  | @ -82,6 +82,7 @@ type OAuthProxy struct { | |||
| 	SkipProviderButton  bool | ||||
| 	skipAuthPreflight   bool | ||||
| 	skipJwtBearerTokens bool | ||||
| 	forceJSONErrors     bool | ||||
| 	realClientIPParser  ipapi.RealClientIPParser | ||||
| 	trustedIPs          *ip.NetSet | ||||
| 
 | ||||
|  | @ -198,6 +199,7 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr | |||
| 		skipJwtBearerTokens: opts.SkipJwtBearerTokens, | ||||
| 		realClientIPParser:  opts.GetRealClientIPParser(), | ||||
| 		SkipProviderButton:  opts.SkipProviderButton, | ||||
| 		forceJSONErrors:     opts.ForceJSONErrors, | ||||
| 		trustedIPs:          trustedIPs, | ||||
| 
 | ||||
| 		basicAuthValidator: basicAuthValidator, | ||||
|  | @ -850,7 +852,7 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) { | |||
| 		p.headersChain.Then(p.upstreamProxy).ServeHTTP(rw, req) | ||||
| 	case ErrNeedsLogin: | ||||
| 		// we need to send the user to a login screen
 | ||||
| 		if isAjax(req) { | ||||
| 		if p.forceJSONErrors || isAjax(req) { | ||||
| 			// no point redirecting an AJAX request
 | ||||
| 			p.errorJSON(rw, http.StatusUnauthorized) | ||||
| 			return | ||||
|  | @ -863,7 +865,11 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) { | |||
| 		} | ||||
| 
 | ||||
| 	case ErrAccessDenied: | ||||
| 		p.ErrorPage(rw, req, http.StatusForbidden, "The session failed authorization checks") | ||||
| 		if p.forceJSONErrors { | ||||
| 			p.errorJSON(rw, http.StatusForbidden) | ||||
| 		} else { | ||||
| 			p.ErrorPage(rw, req, http.StatusForbidden, "The session failed authorization checks") | ||||
| 		} | ||||
| 
 | ||||
| 	default: | ||||
| 		// unknown error
 | ||||
|  | @ -1056,4 +1062,7 @@ func isAjax(req *http.Request) bool { | |||
| func (p *OAuthProxy) errorJSON(rw http.ResponseWriter, code int) { | ||||
| 	rw.Header().Set("Content-Type", applicationJSON) | ||||
| 	rw.WriteHeader(code) | ||||
| 	// we need to send some JSON response because we set the Content-Type to
 | ||||
| 	// application/json
 | ||||
| 	rw.Write([]byte("{}")) | ||||
| } | ||||
|  |  | |||
|  | @ -1535,9 +1535,10 @@ type ajaxRequestTest struct { | |||
| 	proxy *OAuthProxy | ||||
| } | ||||
| 
 | ||||
| func newAjaxRequestTest() (*ajaxRequestTest, error) { | ||||
| func newAjaxRequestTest(forceJSONErrors bool) (*ajaxRequestTest, error) { | ||||
| 	test := &ajaxRequestTest{} | ||||
| 	test.opts = baseTestOptions() | ||||
| 	test.opts.ForceJSONErrors = forceJSONErrors | ||||
| 	err := validation.Validate(test.opts) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
|  | @ -1552,59 +1553,64 @@ func newAjaxRequestTest() (*ajaxRequestTest, error) { | |||
| 	return test, nil | ||||
| } | ||||
| 
 | ||||
| func (test *ajaxRequestTest) getEndpoint(endpoint string, header http.Header) (int, http.Header, error) { | ||||
| func (test *ajaxRequestTest) getEndpoint(endpoint string, header http.Header) (int, http.Header, []byte, error) { | ||||
| 	rw := httptest.NewRecorder() | ||||
| 	req, err := http.NewRequest(http.MethodGet, endpoint, strings.NewReader("")) | ||||
| 	if err != nil { | ||||
| 		return 0, nil, err | ||||
| 		return 0, nil, nil, err | ||||
| 	} | ||||
| 	req.Header = header | ||||
| 	test.proxy.ServeHTTP(rw, req) | ||||
| 	return rw.Code, rw.Header(), nil | ||||
| 	return rw.Code, rw.Header(), rw.Body.Bytes(), nil | ||||
| } | ||||
| 
 | ||||
| func testAjaxUnauthorizedRequest(t *testing.T, header http.Header) { | ||||
| 	test, err := newAjaxRequestTest() | ||||
| func testAjaxUnauthorizedRequest(t *testing.T, header http.Header, forceJSONErrors bool) { | ||||
| 	test, err := newAjaxRequestTest(forceJSONErrors) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	endpoint := "/test" | ||||
| 
 | ||||
| 	code, rh, err := test.getEndpoint(endpoint, header) | ||||
| 	code, rh, body, err := test.getEndpoint(endpoint, header) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, http.StatusUnauthorized, code) | ||||
| 	mime := rh.Get("Content-Type") | ||||
| 	assert.Equal(t, applicationJSON, mime) | ||||
| 	assert.Equal(t, []byte("{}"), body) | ||||
| } | ||||
| func TestAjaxUnauthorizedRequest1(t *testing.T) { | ||||
| 	header := make(http.Header) | ||||
| 	header.Add("accept", applicationJSON) | ||||
| 
 | ||||
| 	testAjaxUnauthorizedRequest(t, header) | ||||
| 	testAjaxUnauthorizedRequest(t, header, false) | ||||
| } | ||||
| 
 | ||||
| func TestAjaxUnauthorizedRequest2(t *testing.T) { | ||||
| 	header := make(http.Header) | ||||
| 	header.Add("Accept", applicationJSON) | ||||
| 
 | ||||
| 	testAjaxUnauthorizedRequest(t, header) | ||||
| 	testAjaxUnauthorizedRequest(t, header, false) | ||||
| } | ||||
| 
 | ||||
| func TestAjaxUnauthorizedRequestAccept1(t *testing.T) { | ||||
| 	header := make(http.Header) | ||||
| 	header.Add("Accept", "application/json, text/plain, */*") | ||||
| 
 | ||||
| 	testAjaxUnauthorizedRequest(t, header) | ||||
| 	testAjaxUnauthorizedRequest(t, header, false) | ||||
| } | ||||
| 
 | ||||
| func TestForceJSONErrorsUnauthorizedRequest(t *testing.T) { | ||||
| 	testAjaxUnauthorizedRequest(t, nil, true) | ||||
| } | ||||
| 
 | ||||
| func TestAjaxForbiddendRequest(t *testing.T) { | ||||
| 	test, err := newAjaxRequestTest() | ||||
| 	test, err := newAjaxRequestTest(false) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	endpoint := "/test" | ||||
| 	header := make(http.Header) | ||||
| 	code, rh, err := test.getEndpoint(endpoint, header) | ||||
| 	code, rh, _, err := test.getEndpoint(endpoint, header) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, http.StatusForbidden, code) | ||||
| 	mime := rh.Get("Content-Type") | ||||
|  |  | |||
|  | @ -58,6 +58,7 @@ type Options struct { | |||
| 	SkipProviderButton    bool     `flag:"skip-provider-button" cfg:"skip_provider_button"` | ||||
| 	SSLInsecureSkipVerify bool     `flag:"ssl-insecure-skip-verify" cfg:"ssl_insecure_skip_verify"` | ||||
| 	SkipAuthPreflight     bool     `flag:"skip-auth-preflight" cfg:"skip_auth_preflight"` | ||||
| 	ForceJSONErrors       bool     `flag:"force-json-errors" cfg:"force_json_errors"` | ||||
| 
 | ||||
| 	SignatureKey    string `flag:"signature-key" cfg:"signature_key"` | ||||
| 	GCPHealthChecks bool   `flag:"gcp-healthchecks" cfg:"gcp_healthchecks"` | ||||
|  | @ -121,6 +122,7 @@ func NewFlagSet() *pflag.FlagSet { | |||
| 	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 providers") | ||||
| 	flagSet.Bool("skip-jwt-bearer-tokens", false, "will skip requests that have verified JWT bearer tokens (default false)") | ||||
| 	flagSet.Bool("force-json-errors", false, "will force JSON errors instead of HTTP error pages or redirects") | ||||
| 	flagSet.StringSlice("extra-jwt-issuers", []string{}, "if skip-jwt-bearer-tokens is set, a list of extra JWT issuer=audience pairs (where the issuer URL has a .well-known/openid-configuration or a .well-known/jwks.json)") | ||||
| 
 | ||||
| 	flagSet.StringSlice("email-domain", []string{}, "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email") | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue