Merge bad29b556e into 9168731c7a
				
					
				
			This commit is contained in:
		
						commit
						d08534f8d3
					
				
							
								
								
									
										133
									
								
								oauthproxy.go
								
								
								
								
							
							
						
						
									
										133
									
								
								oauthproxy.go
								
								
								
								
							|  | @ -13,6 +13,8 @@ import ( | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/signal" | 	"os/signal" | ||||||
| 	"regexp" | 	"regexp" | ||||||
|  | 	"slices" | ||||||
|  | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"time" | 	"time" | ||||||
|  | @ -113,7 +115,10 @@ type OAuthProxy struct { | ||||||
| 	redirectValidator redirect.Validator | 	redirectValidator redirect.Validator | ||||||
| 	appDirector       redirect.AppDirector | 	appDirector       redirect.AppDirector | ||||||
| 
 | 
 | ||||||
| 	encodeState bool | 	encodeState         bool | ||||||
|  | 	maxAutomatedRetries int | ||||||
|  | 	idpErrorsToRetry    []string | ||||||
|  | 	retryCsrfErrors     bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewOAuthProxy creates a new instance of OAuthProxy from the options provided
 | // NewOAuthProxy creates a new instance of OAuthProxy from the options provided
 | ||||||
|  | @ -238,16 +243,19 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr | ||||||
| 		allowQuerySemicolons: opts.AllowQuerySemicolons, | 		allowQuerySemicolons: opts.AllowQuerySemicolons, | ||||||
| 		trustedIPs:           trustedIPs, | 		trustedIPs:           trustedIPs, | ||||||
| 
 | 
 | ||||||
| 		basicAuthValidator: basicAuthValidator, | 		basicAuthValidator:  basicAuthValidator, | ||||||
| 		basicAuthGroups:    opts.HtpasswdUserGroups, | 		basicAuthGroups:     opts.HtpasswdUserGroups, | ||||||
| 		sessionChain:       sessionChain, | 		sessionChain:        sessionChain, | ||||||
| 		headersChain:       headersChain, | 		headersChain:        headersChain, | ||||||
| 		preAuthChain:       preAuthChain, | 		preAuthChain:        preAuthChain, | ||||||
| 		pageWriter:         pageWriter, | 		pageWriter:          pageWriter, | ||||||
| 		upstreamProxy:      upstreamProxy, | 		upstreamProxy:       upstreamProxy, | ||||||
| 		redirectValidator:  redirectValidator, | 		redirectValidator:   redirectValidator, | ||||||
| 		appDirector:        appDirector, | 		appDirector:         appDirector, | ||||||
| 		encodeState:        opts.EncodeState, | 		encodeState:         opts.EncodeState, | ||||||
|  | 		maxAutomatedRetries: opts.MaxAutomatedRetries, | ||||||
|  | 		idpErrorsToRetry:    opts.IdpErrorsToRetry, | ||||||
|  | 		retryCsrfErrors:     opts.RetryCsrfErrors, | ||||||
| 	} | 	} | ||||||
| 	p.buildServeMux(opts.ProxyPrefix) | 	p.buildServeMux(opts.ProxyPrefix) | ||||||
| 
 | 
 | ||||||
|  | @ -788,13 +796,32 @@ func (p *OAuthProxy) backendLogout(rw http.ResponseWriter, req *http.Request) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // OAuthRestart restarts the OAuth2 authentication flow with a specified retryCounter
 | ||||||
|  | func (p *OAuthProxy) OAuthRestart(rw http.ResponseWriter, req *http.Request, redirectPath string, retryCount int) (error, string) { | ||||||
|  | 	if !p.redirectValidator.IsValidRedirect(redirectPath) { | ||||||
|  | 		return errors.New("invalid redirect"), fmt.Sprintf("Login Failed: The given redirect (%v) was not valid. Please try again.", redirectPath) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if retryCount >= p.maxAutomatedRetries { | ||||||
|  | 		return errors.New("retries exceeded"), fmt.Sprintf("Login Failed: The maximum amount (%v) of automated retries exceeded.", p.maxAutomatedRetries) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	logger.Printf("Restarting Oauth2 flow (%v/%v)", retryCount, p.maxAutomatedRetries) | ||||||
|  | 	// Handlers typically use this parameter to preserve the login redirect path.
 | ||||||
|  | 	// Since initialization now occurs within the proxy, we must set this parameter
 | ||||||
|  | 	// to retain the originally requested path for restarted requests.
 | ||||||
|  | 	req.Form.Set("rd", redirectPath) | ||||||
|  | 	p.doOAuthStart(rw, req, nil, retryCount+1) | ||||||
|  | 	return nil, "" | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // OAuthStart starts the OAuth2 authentication flow
 | // OAuthStart starts the OAuth2 authentication flow
 | ||||||
| func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) { | func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) { | ||||||
| 	// start the flow permitting login URL query parameters to be overridden from the request URL
 | 	// start the flow permitting login URL query parameters to be overridden from the request URL
 | ||||||
| 	p.doOAuthStart(rw, req, req.URL.Query()) | 	p.doOAuthStart(rw, req, req.URL.Query(), 0) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *OAuthProxy) doOAuthStart(rw http.ResponseWriter, req *http.Request, overrides url.Values) { | func (p *OAuthProxy) doOAuthStart(rw http.ResponseWriter, req *http.Request, overrides url.Values, retryCount int) { | ||||||
| 	extraParams := p.provider.Data().LoginURLParams(overrides) | 	extraParams := p.provider.Data().LoginURLParams(overrides) | ||||||
| 	prepareNoCache(rw) | 	prepareNoCache(rw) | ||||||
| 
 | 
 | ||||||
|  | @ -839,7 +866,7 @@ func (p *OAuthProxy) doOAuthStart(rw http.ResponseWriter, req *http.Request, ove | ||||||
| 	callbackRedirect := p.getOAuthRedirectURI(req) | 	callbackRedirect := p.getOAuthRedirectURI(req) | ||||||
| 	loginURL := p.provider.GetLoginURL( | 	loginURL := p.provider.GetLoginURL( | ||||||
| 		callbackRedirect, | 		callbackRedirect, | ||||||
| 		encodeState(csrf.HashOAuthState(), appRedirect, p.encodeState), | 		encodeState(csrf.HashOAuthState(), appRedirect, retryCount, p.encodeState), | ||||||
| 		csrf.HashOIDCNonce(), | 		csrf.HashOIDCNonce(), | ||||||
| 		extraParams, | 		extraParams, | ||||||
| 	) | 	) | ||||||
|  | @ -864,22 +891,33 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) { | ||||||
| 		p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error()) | 		p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error()) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	errorString := req.Form.Get("error") |  | ||||||
| 	if errorString != "" { |  | ||||||
| 		logger.Errorf("Error while parsing OAuth2 callback: %s", errorString) |  | ||||||
| 		message := fmt.Sprintf("Login Failed: The upstream identity provider returned an error: %s", errorString) |  | ||||||
| 		// Set the debug message and override the non debug message to be the same for this case
 |  | ||||||
| 		p.ErrorPage(rw, req, http.StatusForbidden, message, message) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	nonce, appRedirect, err := decodeState(req.Form.Get("state"), p.encodeState) | 	nonce, appRedirect, retries, err := decodeState(req.Form.Get("state"), p.encodeState) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		logger.Errorf("Error while parsing OAuth2 state: %v", err) | 		logger.Errorf("Error while parsing OAuth2 state: %v", err) | ||||||
| 		p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error()) | 		p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error()) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	errorString := req.Form.Get("error") | ||||||
|  | 	if errorString != "" { | ||||||
|  | 		message := fmt.Sprintf("Login Failed: The upstream identity provider returned an error: %s", errorString) | ||||||
|  | 		logger.Error(message) | ||||||
|  | 
 | ||||||
|  | 		if slices.Contains(p.idpErrorsToRetry, errorString) { | ||||||
|  | 			restartErr, restartErrString := p.OAuthRestart(rw, req, appRedirect, retries) | ||||||
|  | 
 | ||||||
|  | 			if restartErr != nil { | ||||||
|  | 				logger.Errorf("Encountered Error while restarting: %s", restartErr) | ||||||
|  | 				p.ErrorPage(rw, req, http.StatusForbidden, restartErr.Error(), message, restartErrString) | ||||||
|  | 			} | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		// Set the debug message and override the non debug message to be the same for this case
 | ||||||
|  | 		p.ErrorPage(rw, req, http.StatusForbidden, message, message) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// calculate the cookie name
 | 	// calculate the cookie name
 | ||||||
| 	cookieName := cookies.GenerateCookieName(p.CookieOptions, nonce) | 	cookieName := cookies.GenerateCookieName(p.CookieOptions, nonce) | ||||||
| 	// Try to find the CSRF cookie and decode it
 | 	// Try to find the CSRF cookie and decode it
 | ||||||
|  | @ -888,8 +926,16 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) { | ||||||
| 		// There are a lot of issues opened complaining about missing CSRF cookies.
 | 		// There are a lot of issues opened complaining about missing CSRF cookies.
 | ||||||
| 		// Try to log the INs and OUTs of OAuthProxy, to be easier to analyse these issues.
 | 		// Try to log the INs and OUTs of OAuthProxy, to be easier to analyse these issues.
 | ||||||
| 		LoggingCSRFCookiesInOAuthCallback(req, cookieName) | 		LoggingCSRFCookiesInOAuthCallback(req, cookieName) | ||||||
| 		logger.Println(req, logger.AuthFailure, "Invalid authentication via OAuth2: unable to obtain CSRF cookie: %s (state=%s)", err, nonce) | 		message := fmt.Sprintf("Invalid authentication via OAuth2: unable to obtain CSRF cookie: %s (state=%s)", err, nonce) | ||||||
| 		p.ErrorPage(rw, req, http.StatusForbidden, err.Error(), "Login Failed: Unable to find a valid CSRF token. Please try again.") | 		logger.Println(req, logger.AuthFailure, message) | ||||||
|  | 
 | ||||||
|  | 		restartErr, restartErrString := p.OAuthRestart(rw, req, appRedirect, retries) | ||||||
|  | 
 | ||||||
|  | 		if restartErr != nil { | ||||||
|  | 			logger.Errorf("Encountered Error while restarting: %s", restartErr) | ||||||
|  | 			p.ErrorPage(rw, req, http.StatusForbidden, restartErr.Error(), message, restartErrString) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -932,7 +978,7 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) { | ||||||
| 		logger.Errorf("Error with authorization: %v", err) | 		logger.Errorf("Error with authorization: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if p.Validator(session.Email) && authorized { | 	if p.Validator(session.Email) && authorized { | ||||||
| 		logger.PrintAuthf(session.Email, req, logger.AuthSuccess, "Authenticated via OAuth2: %s", session) | 		logger.PrintAuthf(session.Email, req, logger.AuthSuccess, "Authenticated via OAuth2 after %v retries: %s", retries, session) | ||||||
| 		err := p.SaveSession(rw, req, session) | 		err := p.SaveSession(rw, req, session) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			logger.Errorf("Error saving session state for %s: %v", remoteAddr, err) | 			logger.Errorf("Error saving session state for %s: %v", remoteAddr, err) | ||||||
|  | @ -1029,7 +1075,7 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) { | ||||||
| 			// start OAuth flow, but only with the default login URL params - do not
 | 			// start OAuth flow, but only with the default login URL params - do not
 | ||||||
| 			// consider this request's query params as potential overrides, since
 | 			// consider this request's query params as potential overrides, since
 | ||||||
| 			// the user did not explicitly start the login flow
 | 			// the user did not explicitly start the login flow
 | ||||||
| 			p.doOAuthStart(rw, req, nil) | 			p.doOAuthStart(rw, req, nil, 0) | ||||||
| 		} else { | 		} else { | ||||||
| 			p.SignInPage(rw, req, http.StatusForbidden) | 			p.SignInPage(rw, req, http.StatusForbidden) | ||||||
| 		} | 		} | ||||||
|  | @ -1242,8 +1288,9 @@ func checkAllowedEmails(req *http.Request, s *sessionsapi.SessionState) bool { | ||||||
| 
 | 
 | ||||||
| // encodeState builds the OAuth state param out of our nonce and
 | // encodeState builds the OAuth state param out of our nonce and
 | ||||||
| // original application redirect
 | // original application redirect
 | ||||||
| func encodeState(nonce string, redirect string, encode bool) string { | func encodeState(nonce string, redirect string, retryCount int, encode bool) string { | ||||||
| 	rawString := fmt.Sprintf("%v:%v", nonce, redirect) | 	redirectEscaped := url.QueryEscape(redirect) | ||||||
|  | 	rawString := fmt.Sprintf("%v:%v:%v", nonce, redirectEscaped, retryCount) | ||||||
| 	if encode { | 	if encode { | ||||||
| 		return base64.RawURLEncoding.EncodeToString([]byte(rawString)) | 		return base64.RawURLEncoding.EncodeToString([]byte(rawString)) | ||||||
| 	} | 	} | ||||||
|  | @ -1252,18 +1299,36 @@ func encodeState(nonce string, redirect string, encode bool) string { | ||||||
| 
 | 
 | ||||||
| // decodeState splits the reflected OAuth state response back into
 | // decodeState splits the reflected OAuth state response back into
 | ||||||
| // the nonce and original application redirect
 | // the nonce and original application redirect
 | ||||||
| func decodeState(state string, encode bool) (string, string, error) { | func decodeState(state string, encode bool) (string, string, int, error) { | ||||||
| 	toParse := state | 	toParse := state | ||||||
| 	if encode { | 	if encode { | ||||||
| 		decoded, _ := base64.RawURLEncoding.DecodeString(state) | 		decoded, _ := base64.RawURLEncoding.DecodeString(state) | ||||||
| 		toParse = string(decoded) | 		toParse = string(decoded) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	parsedState := strings.SplitN(toParse, ":", 2) | 	parsedState := strings.Split(toParse, ":") | ||||||
| 	if len(parsedState) != 2 { | 	numStateParams := len(parsedState) | ||||||
| 		return "", "", errors.New("invalid length") | 
 | ||||||
|  | 	if numStateParams < 2 || numStateParams > 3 { | ||||||
|  | 		return "", "", -1, errors.New(fmt.Sprintf("invalid number of state parameters (%v)", numStateParams)) | ||||||
| 	} | 	} | ||||||
| 	return parsedState[0], parsedState[1], nil | 
 | ||||||
|  | 	nonce := parsedState[0] | ||||||
|  | 
 | ||||||
|  | 	redirectPath, err := url.QueryUnescape(parsedState[1]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", "", -1, errors.New(fmt.Sprintf("invalid redirectPath url (%v)", parsedState[1])) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	retries := 0 | ||||||
|  | 	if numStateParams > 2 { | ||||||
|  | 		retries, err = strconv.Atoi(parsedState[2]) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return "", "", -1, errors.New(fmt.Sprintf("invalid retry count (%v)", parsedState[2])) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nonce, redirectPath, retries, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // addHeadersForProxying adds the appropriate headers the request / response for proxying
 | // addHeadersForProxying adds the appropriate headers the request / response for proxying
 | ||||||
|  |  | ||||||
|  | @ -413,7 +413,7 @@ func (patTest *PassAccessTokenTest) getCallbackEndpoint() (httpCode int, cookie | ||||||
| 		http.MethodGet, | 		http.MethodGet, | ||||||
| 		fmt.Sprintf( | 		fmt.Sprintf( | ||||||
| 			"/oauth2/callback?code=callback_code&state=%s", | 			"/oauth2/callback?code=callback_code&state=%s", | ||||||
| 			encodeState(csrf.HashOAuthState(), "%2F", false), | 			encodeState(csrf.HashOAuthState(), "%2F", 0, false), | ||||||
| 		), | 		), | ||||||
| 		strings.NewReader(""), | 		strings.NewReader(""), | ||||||
| 	) | 	) | ||||||
|  | @ -3294,24 +3294,61 @@ func TestAuthOnlyAllowedEmailDomains(t *testing.T) { | ||||||
| func TestStateEncodesCorrectly(t *testing.T) { | func TestStateEncodesCorrectly(t *testing.T) { | ||||||
| 	state := "some_state_to_test" | 	state := "some_state_to_test" | ||||||
| 	nonce := "some_nonce_to_test" | 	nonce := "some_nonce_to_test" | ||||||
|  | 	retryCount := 3 | ||||||
| 
 | 
 | ||||||
| 	encodedResult := encodeState(nonce, state, true) | 	encodedResult := encodeState(nonce, state, retryCount, true) | ||||||
| 	assert.Equal(t, "c29tZV9ub25jZV90b190ZXN0OnNvbWVfc3RhdGVfdG9fdGVzdA", encodedResult) | 	assert.Equal(t, "c29tZV9ub25jZV90b190ZXN0OnNvbWVfc3RhdGVfdG9fdGVzdDoz", encodedResult) | ||||||
| 
 | 
 | ||||||
| 	notEncodedResult := encodeState(nonce, state, false) | 	notEncodedResult := encodeState(nonce, state, retryCount, false) | ||||||
| 	assert.Equal(t, "some_nonce_to_test:some_state_to_test", notEncodedResult) | 	assert.Equal(t, "some_nonce_to_test:some_state_to_test:3", notEncodedResult) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestStateEncodesWithColonInRedirect(t *testing.T) { | ||||||
|  | 	redirect := "test:url" | ||||||
|  | 	nonce := "nonce_value" | ||||||
|  | 	retryCount := 3 | ||||||
|  | 
 | ||||||
|  | 	encodedResult := encodeState(nonce, redirect, retryCount, true) | ||||||
|  | 	assert.Equal(t, "bm9uY2VfdmFsdWU6dGVzdCUzQXVybDoz", encodedResult) | ||||||
|  | 
 | ||||||
|  | 	notEncodedResult := encodeState(nonce, redirect, retryCount, false) | ||||||
|  | 	assert.Equal(t, "nonce_value:test%3Aurl:3", notEncodedResult) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestStateDecodesCorrectly(t *testing.T) { | func TestStateDecodesCorrectly(t *testing.T) { | ||||||
| 	nonce, redirect, _ := decodeState("c29tZV9ub25jZV90b190ZXN0OnNvbWVfc3RhdGVfdG9fdGVzdA", true) | 	nonce, redirect, retryCount, _ := decodeState("c29tZV9ub25jZV90b190ZXN0OnNvbWVfc3RhdGVfdG9fdGVzdDoz", true) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal(t, "some_nonce_to_test", nonce) | 	assert.Equal(t, "some_nonce_to_test", nonce) | ||||||
| 	assert.Equal(t, "some_state_to_test", redirect) | 	assert.Equal(t, "some_state_to_test", redirect) | ||||||
|  | 	assert.Equal(t, 3, retryCount) | ||||||
| 
 | 
 | ||||||
| 	nonce2, redirect2, _ := decodeState("some_nonce_to_test:some_state_to_test", false) | 	nonce2, redirect2, retryCount2, _ := decodeState("some_nonce_to_test:some_state_to_test:3", false) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal(t, "some_nonce_to_test", nonce2) | 	assert.Equal(t, "some_nonce_to_test", nonce2) | ||||||
| 	assert.Equal(t, "some_state_to_test", redirect2) | 	assert.Equal(t, "some_state_to_test", redirect2) | ||||||
|  | 	assert.Equal(t, 3, retryCount2) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestStateDecodesWithColonInRedirect(t *testing.T) { | ||||||
|  | 	nonce, redirect, retryCount, _ := decodeState("bm9uY2VfdmFsdWU6dGVzdCUzQXVybDoz", true) | ||||||
|  | 
 | ||||||
|  | 	assert.Equal(t, "nonce_value", nonce) | ||||||
|  | 	assert.Equal(t, "test:url", redirect) | ||||||
|  | 	assert.Equal(t, 3, retryCount) | ||||||
|  | 
 | ||||||
|  | 	nonce2, redirect2, retryCount2, _ := decodeState("nonce_value:test%3Aurl:3", false) | ||||||
|  | 
 | ||||||
|  | 	assert.Equal(t, "nonce_value", nonce2) | ||||||
|  | 	assert.Equal(t, "test:url", redirect2) | ||||||
|  | 	assert.Equal(t, 3, retryCount2) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestStateDecodesWithoutRetryCount(t *testing.T) { | ||||||
|  | 	nonce, redirect, retryCount, _ := decodeState("nonce_value:url", false) | ||||||
|  | 
 | ||||||
|  | 	assert.Equal(t, "nonce_value", nonce) | ||||||
|  | 	assert.Equal(t, "url", redirect) | ||||||
|  | 	assert.Equal(t, 0, retryCount) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestAuthOnlyAllowedEmails(t *testing.T) { | func TestAuthOnlyAllowedEmails(t *testing.T) { | ||||||
|  |  | ||||||
|  | @ -34,6 +34,9 @@ type Options struct { | ||||||
| 	WhitelistDomains        []string `flag:"whitelist-domain" cfg:"whitelist_domains"` | 	WhitelistDomains        []string `flag:"whitelist-domain" cfg:"whitelist_domains"` | ||||||
| 	HtpasswdFile            string   `flag:"htpasswd-file" cfg:"htpasswd_file"` | 	HtpasswdFile            string   `flag:"htpasswd-file" cfg:"htpasswd_file"` | ||||||
| 	HtpasswdUserGroups      []string `flag:"htpasswd-user-group" cfg:"htpasswd_user_groups"` | 	HtpasswdUserGroups      []string `flag:"htpasswd-user-group" cfg:"htpasswd_user_groups"` | ||||||
|  | 	MaxAutomatedRetries     int      `flag:"max-automated-retries" cfg:"max_automated_retries"` | ||||||
|  | 	IdpErrorsToRetry        []string `flag:"retry-idp-errors" cfg:"retry_idp_errors"` | ||||||
|  | 	RetryCsrfErrors         bool     `flag:"retry-csrf-errors" cfg:"retry_csrf_errors"` | ||||||
| 
 | 
 | ||||||
| 	Cookie    Cookie         `cfg:",squash"` | 	Cookie    Cookie         `cfg:",squash"` | ||||||
| 	Session   SessionOptions `cfg:",squash"` | 	Session   SessionOptions `cfg:",squash"` | ||||||
|  | @ -161,6 +164,9 @@ func NewFlagSet() *pflag.FlagSet { | ||||||
| 	flagSet.Int("redis-connection-idle-timeout", 0, "Redis connection idle timeout seconds, if Redis timeout option is non-zero, the --redis-connection-idle-timeout must be less then Redis timeout option") | 	flagSet.Int("redis-connection-idle-timeout", 0, "Redis connection idle timeout seconds, if Redis timeout option is non-zero, the --redis-connection-idle-timeout must be less then Redis timeout option") | ||||||
| 	flagSet.String("signature-key", "", "GAP-Signature request signature key (algorithm:secretkey)") | 	flagSet.String("signature-key", "", "GAP-Signature request signature key (algorithm:secretkey)") | ||||||
| 	flagSet.Bool("gcp-healthchecks", false, "Enable GCP/GKE healthcheck endpoints") | 	flagSet.Bool("gcp-healthchecks", false, "Enable GCP/GKE healthcheck endpoints") | ||||||
|  | 	flagSet.Int("max-automated-retries", 0, "Maximum number of automated retries for callback errors") | ||||||
|  | 	flagSet.StringSlice("retry-idp-errors", []string{}, "Errors from IdP that should be automatically retried") | ||||||
|  | 	flagSet.Bool("retry-csrf-errors", false, "If true retries Csrf errors automatically") | ||||||
| 
 | 
 | ||||||
| 	flagSet.AddFlagSet(cookieFlagSet()) | 	flagSet.AddFlagSet(cookieFlagSet()) | ||||||
| 	flagSet.AddFlagSet(loggingFlagSet()) | 	flagSet.AddFlagSet(loggingFlagSet()) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue