diff --git a/auth/auth_context.go b/auth/auth_context.go index 0e3ec07b2..cde008d10 100644 --- a/auth/auth_context.go +++ b/auth/auth_context.go @@ -133,6 +133,11 @@ func NewAuthenticationContext(ctx context.Context, sm core.SecretManager, oauth2 Timeout: IdpConnectionTimeout, } + if len(options.UserAuth.HTTPProxyURL.String()) > 0 { + logger.Infof(ctx, "HTTPProxy URL for OAuth2 is: %s", options.UserAuth.HTTPProxyURL.String()) + httpClient.Transport = &http.Transport{Proxy: http.ProxyURL(&options.UserAuth.HTTPProxyURL.URL)} + } + // Construct an oidc Provider, which needs its own http Client. oidcCtx := oidc.ClientContext(ctx, httpClient) baseURL := options.UserAuth.OpenID.BaseURL.String() diff --git a/auth/config/config.go b/auth/config/config.go index eca659fe1..a42365205 100644 --- a/auth/config/config.go +++ b/auth/config/config.go @@ -215,6 +215,9 @@ type UserAuthConfig struct { OpenID OpenIDOptions `json:"openId" pflag:",OpenID Configuration for User Auth"` // Possibly add basicAuth & SAML/p support. + // HTTPProxyURL allows operators to access external OAuth2 servers using an external HTTP Proxy + HTTPProxyURL config.URL `json:"httpProxyURL" pflag:",OPTIONAL: HTTP Proxy to be used for OAuth requests."` + // Secret names, defaults are set in DefaultConfig variable above but are possible to override through configs. CookieHashKeySecretName string `json:"cookieHashKeySecretName" pflag:",OPTIONAL: Secret name to use for cookie hash key."` CookieBlockKeySecretName string `json:"cookieBlockKeySecretName" pflag:",OPTIONAL: Secret name to use for cookie block key."` diff --git a/auth/config/config_flags.go b/auth/config/config_flags.go index 2a2750406..b84be106f 100755 --- a/auth/config/config_flags.go +++ b/auth/config/config_flags.go @@ -60,6 +60,7 @@ func (cfg Config) GetPFlagSet(prefix string) *pflag.FlagSet { cmdFlags.String(fmt.Sprintf("%v%v", prefix, "userAuth.openId.clientSecretFile"), DefaultConfig.UserAuth.OpenID.DeprecatedClientSecretFile, "") cmdFlags.String(fmt.Sprintf("%v%v", prefix, "userAuth.openId.baseUrl"), DefaultConfig.UserAuth.OpenID.BaseURL.String(), "") cmdFlags.StringSlice(fmt.Sprintf("%v%v", prefix, "userAuth.openId.scopes"), DefaultConfig.UserAuth.OpenID.Scopes, "") + cmdFlags.String(fmt.Sprintf("%v%v", prefix, "userAuth.httpProxyURL"), DefaultConfig.UserAuth.HTTPProxyURL.String(), "OPTIONAL: HTTP Proxy to be used for OAuth requests.") cmdFlags.String(fmt.Sprintf("%v%v", prefix, "userAuth.cookieHashKeySecretName"), DefaultConfig.UserAuth.CookieHashKeySecretName, "OPTIONAL: Secret name to use for cookie hash key.") cmdFlags.String(fmt.Sprintf("%v%v", prefix, "userAuth.cookieBlockKeySecretName"), DefaultConfig.UserAuth.CookieBlockKeySecretName, "OPTIONAL: Secret name to use for cookie block key.") cmdFlags.String(fmt.Sprintf("%v%v", prefix, "userAuth.cookieSetting.sameSitePolicy"), DefaultConfig.UserAuth.CookieSetting.SameSitePolicy.String(), "OPTIONAL: Allows you to declare if your cookie should be restricted to a first-party or same-site context.Wrapper around http.SameSite.") diff --git a/auth/config/config_flags_test.go b/auth/config/config_flags_test.go index 5e835f66e..ffcb653d8 100755 --- a/auth/config/config_flags_test.go +++ b/auth/config/config_flags_test.go @@ -239,6 +239,20 @@ func TestConfig_SetFlags(t *testing.T) { } }) }) + t.Run("Test_userAuth.httpProxyURL", func(t *testing.T) { + + t.Run("Override", func(t *testing.T) { + testValue := DefaultConfig.UserAuth.HTTPProxyURL.String() + + cmdFlags.Set("userAuth.httpProxyURL", testValue) + if vString, err := cmdFlags.GetString("userAuth.httpProxyURL"); err == nil { + testDecodeJson_Config(t, fmt.Sprintf("%v", vString), &actual.UserAuth.HTTPProxyURL) + + } else { + assert.FailNow(t, err.Error()) + } + }) + }) t.Run("Test_userAuth.cookieHashKeySecretName", func(t *testing.T) { t.Run("Override", func(t *testing.T) { diff --git a/auth/handlers.go b/auth/handlers.go index bc6c37208..764b56265 100644 --- a/auth/handlers.go +++ b/auth/handlers.go @@ -57,6 +57,7 @@ func RegisterHandlers(ctx context.Context, handler interfaces.HandlerRegisterer, func RefreshTokensIfExists(ctx context.Context, authCtx interfaces.AuthenticationContext, authHandler http.HandlerFunc) http.HandlerFunc { return func(writer http.ResponseWriter, request *http.Request) { + ctx = context.WithValue(ctx, oauth2.HTTPClient, authCtx.GetHTTPClient()) // Since we only do one thing if there are no errors anywhere along the chain, we can save code by just // using one variable and checking for errors at the end. idToken, accessToken, refreshToken, err := authCtx.CookieManager().RetrieveTokenValues(ctx, request) @@ -140,6 +141,8 @@ func GetCallbackHandler(ctx context.Context, authCtx interfaces.AuthenticationCo logger.Debugf(ctx, "Running callback handler... for RequestURI %v", request.RequestURI) authorizationCode := request.FormValue(AuthorizationResponseCodeType) + ctx = context.WithValue(ctx, oauth2.HTTPClient, authCtx.GetHTTPClient()) + err := VerifyCsrfCookie(ctx, request) if err != nil { logger.Errorf(ctx, "Invalid CSRF token cookie %s", err) diff --git a/auth/handlers_test.go b/auth/handlers_test.go index 4919ed5c2..b7cc636e5 100644 --- a/auth/handlers_test.go +++ b/auth/handlers_test.go @@ -43,10 +43,14 @@ func setupMockedAuthContextAtEndpoint(endpoint string) *mocks.AuthenticationCont }, Scopes: []string{"openid", "other"}, } + dummyHTTPClient := &http.Client{ + Timeout: IdpConnectionTimeout, + } mockAuthCtx.OnCookieManagerMatch().Return(mockCookieHandler) mockCookieHandler.OnSetTokenCookiesMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) mockCookieHandler.OnSetUserInfoCookieMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) mockAuthCtx.OnOAuth2ClientConfigMatch(mock.Anything).Return(&dummyOAuth2Config) + mockAuthCtx.OnGetHTTPClient().Return(dummyHTTPClient) return mockAuthCtx }