You've already forked oauth2-proxy
mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-07-01 01:04:59 +02:00
Add force-json-errors flag
This commit is contained in:
@ -21,6 +21,7 @@
|
|||||||
- [#1315](https://github.com/oauth2-proxy/oauth2-proxy/pull/1315) linkedin: Update provider to v2 (@wuurrd)
|
- [#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)
|
- [#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)
|
- [#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)
|
- [#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)
|
- [#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)
|
- [#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}) )
|
COMPREPLY=( $(compgen -W 'X-Real-IP X-Forwarded-For X-ProxyUser-IP' -- ${cur}) )
|
||||||
return 0
|
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
|
return 0
|
||||||
;;
|
;;
|
||||||
esac
|
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) |
|
| `--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"` |
|
| `--flush-interval` | duration | period between flushing response buffers when streaming responses | `"1s"` |
|
||||||
| `--force-https` | bool | enforce https redirect | `false` |
|
| `--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. | |
|
| `--banner` | string | custom (html) banner string. Use `"-"` to disable default banner. | |
|
||||||
| `--footer` | string | custom (html) footer string. Use `"-"` to disable default footer. | |
|
| `--footer` | string | custom (html) footer string. Use `"-"` to disable default footer. | |
|
||||||
| `--github-org` | string | restrict logins to members of this organisation | |
|
| `--github-org` | string | restrict logins to members of this organisation | |
|
||||||
|
@ -82,6 +82,7 @@ type OAuthProxy struct {
|
|||||||
SkipProviderButton bool
|
SkipProviderButton bool
|
||||||
skipAuthPreflight bool
|
skipAuthPreflight bool
|
||||||
skipJwtBearerTokens bool
|
skipJwtBearerTokens bool
|
||||||
|
forceJSONErrors bool
|
||||||
realClientIPParser ipapi.RealClientIPParser
|
realClientIPParser ipapi.RealClientIPParser
|
||||||
trustedIPs *ip.NetSet
|
trustedIPs *ip.NetSet
|
||||||
|
|
||||||
@ -198,6 +199,7 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
|
|||||||
skipJwtBearerTokens: opts.SkipJwtBearerTokens,
|
skipJwtBearerTokens: opts.SkipJwtBearerTokens,
|
||||||
realClientIPParser: opts.GetRealClientIPParser(),
|
realClientIPParser: opts.GetRealClientIPParser(),
|
||||||
SkipProviderButton: opts.SkipProviderButton,
|
SkipProviderButton: opts.SkipProviderButton,
|
||||||
|
forceJSONErrors: opts.ForceJSONErrors,
|
||||||
trustedIPs: trustedIPs,
|
trustedIPs: trustedIPs,
|
||||||
|
|
||||||
basicAuthValidator: basicAuthValidator,
|
basicAuthValidator: basicAuthValidator,
|
||||||
@ -850,7 +852,7 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
|
|||||||
p.headersChain.Then(p.upstreamProxy).ServeHTTP(rw, req)
|
p.headersChain.Then(p.upstreamProxy).ServeHTTP(rw, req)
|
||||||
case ErrNeedsLogin:
|
case ErrNeedsLogin:
|
||||||
// we need to send the user to a login screen
|
// we need to send the user to a login screen
|
||||||
if isAjax(req) {
|
if p.forceJSONErrors || isAjax(req) {
|
||||||
// no point redirecting an AJAX request
|
// no point redirecting an AJAX request
|
||||||
p.errorJSON(rw, http.StatusUnauthorized)
|
p.errorJSON(rw, http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
@ -863,7 +865,11 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case ErrAccessDenied:
|
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:
|
default:
|
||||||
// unknown error
|
// unknown error
|
||||||
@ -1056,4 +1062,7 @@ func isAjax(req *http.Request) bool {
|
|||||||
func (p *OAuthProxy) errorJSON(rw http.ResponseWriter, code int) {
|
func (p *OAuthProxy) errorJSON(rw http.ResponseWriter, code int) {
|
||||||
rw.Header().Set("Content-Type", applicationJSON)
|
rw.Header().Set("Content-Type", applicationJSON)
|
||||||
rw.WriteHeader(code)
|
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
|
proxy *OAuthProxy
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAjaxRequestTest() (*ajaxRequestTest, error) {
|
func newAjaxRequestTest(forceJSONErrors bool) (*ajaxRequestTest, error) {
|
||||||
test := &ajaxRequestTest{}
|
test := &ajaxRequestTest{}
|
||||||
test.opts = baseTestOptions()
|
test.opts = baseTestOptions()
|
||||||
|
test.opts.ForceJSONErrors = forceJSONErrors
|
||||||
err := validation.Validate(test.opts)
|
err := validation.Validate(test.opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1552,59 +1553,64 @@ func newAjaxRequestTest() (*ajaxRequestTest, error) {
|
|||||||
return test, nil
|
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()
|
rw := httptest.NewRecorder()
|
||||||
req, err := http.NewRequest(http.MethodGet, endpoint, strings.NewReader(""))
|
req, err := http.NewRequest(http.MethodGet, endpoint, strings.NewReader(""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, nil, err
|
||||||
}
|
}
|
||||||
req.Header = header
|
req.Header = header
|
||||||
test.proxy.ServeHTTP(rw, req)
|
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) {
|
func testAjaxUnauthorizedRequest(t *testing.T, header http.Header, forceJSONErrors bool) {
|
||||||
test, err := newAjaxRequestTest()
|
test, err := newAjaxRequestTest(forceJSONErrors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
endpoint := "/test"
|
endpoint := "/test"
|
||||||
|
|
||||||
code, rh, err := test.getEndpoint(endpoint, header)
|
code, rh, body, err := test.getEndpoint(endpoint, header)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, http.StatusUnauthorized, code)
|
assert.Equal(t, http.StatusUnauthorized, code)
|
||||||
mime := rh.Get("Content-Type")
|
mime := rh.Get("Content-Type")
|
||||||
assert.Equal(t, applicationJSON, mime)
|
assert.Equal(t, applicationJSON, mime)
|
||||||
|
assert.Equal(t, []byte("{}"), body)
|
||||||
}
|
}
|
||||||
func TestAjaxUnauthorizedRequest1(t *testing.T) {
|
func TestAjaxUnauthorizedRequest1(t *testing.T) {
|
||||||
header := make(http.Header)
|
header := make(http.Header)
|
||||||
header.Add("accept", applicationJSON)
|
header.Add("accept", applicationJSON)
|
||||||
|
|
||||||
testAjaxUnauthorizedRequest(t, header)
|
testAjaxUnauthorizedRequest(t, header, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAjaxUnauthorizedRequest2(t *testing.T) {
|
func TestAjaxUnauthorizedRequest2(t *testing.T) {
|
||||||
header := make(http.Header)
|
header := make(http.Header)
|
||||||
header.Add("Accept", applicationJSON)
|
header.Add("Accept", applicationJSON)
|
||||||
|
|
||||||
testAjaxUnauthorizedRequest(t, header)
|
testAjaxUnauthorizedRequest(t, header, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAjaxUnauthorizedRequestAccept1(t *testing.T) {
|
func TestAjaxUnauthorizedRequestAccept1(t *testing.T) {
|
||||||
header := make(http.Header)
|
header := make(http.Header)
|
||||||
header.Add("Accept", "application/json, text/plain, */*")
|
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) {
|
func TestAjaxForbiddendRequest(t *testing.T) {
|
||||||
test, err := newAjaxRequestTest()
|
test, err := newAjaxRequestTest(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
endpoint := "/test"
|
endpoint := "/test"
|
||||||
header := make(http.Header)
|
header := make(http.Header)
|
||||||
code, rh, err := test.getEndpoint(endpoint, header)
|
code, rh, _, err := test.getEndpoint(endpoint, header)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, http.StatusForbidden, code)
|
assert.Equal(t, http.StatusForbidden, code)
|
||||||
mime := rh.Get("Content-Type")
|
mime := rh.Get("Content-Type")
|
||||||
|
@ -58,6 +58,7 @@ type Options struct {
|
|||||||
SkipProviderButton bool `flag:"skip-provider-button" cfg:"skip_provider_button"`
|
SkipProviderButton bool `flag:"skip-provider-button" cfg:"skip_provider_button"`
|
||||||
SSLInsecureSkipVerify bool `flag:"ssl-insecure-skip-verify" cfg:"ssl_insecure_skip_verify"`
|
SSLInsecureSkipVerify bool `flag:"ssl-insecure-skip-verify" cfg:"ssl_insecure_skip_verify"`
|
||||||
SkipAuthPreflight bool `flag:"skip-auth-preflight" cfg:"skip_auth_preflight"`
|
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"`
|
SignatureKey string `flag:"signature-key" cfg:"signature_key"`
|
||||||
GCPHealthChecks bool `flag:"gcp-healthchecks" cfg:"gcp_healthchecks"`
|
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("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("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("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("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")
|
flagSet.StringSlice("email-domain", []string{}, "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email")
|
||||||
|
Reference in New Issue
Block a user