From 7af06c68706e43574d19b7039c3a281e940d41fd Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Thu, 2 Mar 2023 20:55:19 +0100 Subject: [PATCH] filters/auth/grant: support client id and secret file placeholders This change enables `{host}` placeholder in the client id and secret filenames. E.g. for the request to `foo.example.org` when flag values are `-oauth2-client-id-file=/var/run/secrets/{host}-client-id` and `-oauth2-client-secret-file=/var/run/secrets/{host}-client-secret` the client id and secret files would be `/var/run/secrets/foo.example.org-client-id` and `/var/run/secrets/foo.example.org-client-secret` respectively. Signed-off-by: Alexander Yastrebov --- config/config.go | 6 +- filters/auth/grant.go | 24 ++++- filters/auth/grant_test.go | 177 ++++++++++++++++++++++++++++++++++ filters/auth/grantcallback.go | 18 ++-- filters/auth/grantconfig.go | 141 +++++++++++++++++---------- filters/auth/grantlogout.go | 35 +++---- secrets/file.go | 3 +- skipper.go | 2 + 8 files changed, 315 insertions(+), 91 deletions(-) diff --git a/config/config.go b/config/config.go index 7c9f516f3a..c5c08e50a3 100644 --- a/config/config.go +++ b/config/config.go @@ -451,8 +451,10 @@ func NewConfig() *Config { flag.StringVar(&cfg.Oauth2SecretFile, "oauth2-secret-file", "", "sets the filename with the encryption key for the authentication cookie and grant flow state stored in secrets registry") flag.StringVar(&cfg.Oauth2ClientID, "oauth2-client-id", "", "sets the OAuth2 client id of the current service, used to exchange the access code") flag.StringVar(&cfg.Oauth2ClientSecret, "oauth2-client-secret", "", "sets the OAuth2 client secret associated with the oauth2-client-id, used to exchange the access code") - flag.StringVar(&cfg.Oauth2ClientIDFile, "oauth2-client-id-file", "", "sets the path of the file containing the OAuth2 client id of the current service, used to exchange the access code") - flag.StringVar(&cfg.Oauth2ClientSecretFile, "oauth2-client-secret-file", "", "sets the path of the file containing the OAuth2 client secret associated with the oauth2-client-id, used to exchange the access code") + flag.StringVar(&cfg.Oauth2ClientIDFile, "oauth2-client-id-file", "", "sets the path of the file containing the OAuth2 client id of the current service, used to exchange the access code. "+ + "File name may contain {host} placeholder which will be replaced by the request host") + flag.StringVar(&cfg.Oauth2ClientSecretFile, "oauth2-client-secret-file", "", "sets the path of the file containing the OAuth2 client secret associated with the oauth2-client-id, used to exchange the access code. "+ + "File name may contain {host} placeholder which will be replaced by the request host") flag.StringVar(&cfg.Oauth2CallbackPath, "oauth2-callback-path", "", "sets the path where the OAuth2 callback requests with the authorization code should be redirected to") flag.DurationVar(&cfg.Oauth2TokeninfoTimeout, "oauth2-tokeninfo-timeout", 2*time.Second, "sets the default tokeninfo request timeout duration to 2000ms") flag.IntVar(&cfg.Oauth2TokeninfoCacheSize, "oauth2-tokeninfo-cache-size", 0, "non-zero value enables tokeninfo cache and sets the maximum number of cached tokens") diff --git a/filters/auth/grant.go b/filters/auth/grant.go index 33fec742dc..5918152551 100644 --- a/filters/auth/grant.go +++ b/filters/auth/grant.go @@ -62,6 +62,16 @@ func loginRedirect(ctx filters.FilterContext, config *OAuthConfig) { func loginRedirectWithOverride(ctx filters.FilterContext, config *OAuthConfig, originalOverride string) { req := ctx.Request() + + authConfig, err := config.GetConfig(req) + if err != nil { + log.Errorf("Failed to obtain auth config: %v", err) + ctx.Serve(&http.Response{ + StatusCode: http.StatusForbidden, + }) + return + } + redirect, original := config.RedirectURLs(req) if originalOverride != "" { @@ -75,7 +85,6 @@ func loginRedirectWithOverride(ctx filters.FilterContext, config *OAuthConfig, o return } - authConfig := config.GetConfig() ctx.Serve(&http.Response{ StatusCode: http.StatusTemporaryRedirect, Header: http.Header{ @@ -84,7 +93,7 @@ func loginRedirectWithOverride(ctx filters.FilterContext, config *OAuthConfig, o }) } -func (f *grantFilter) refreshToken(c *cookie) (*oauth2.Token, error) { +func (f *grantFilter) refreshToken(c *cookie, req *http.Request) (*oauth2.Token, error) { // Set the expiry of the token to the past to trigger oauth2.TokenSource // to refresh the access token. token := &oauth2.Token{ @@ -95,9 +104,14 @@ func (f *grantFilter) refreshToken(c *cookie) (*oauth2.Token, error) { ctx := providerContext(f.config) + authConfig, err := f.config.GetConfig(req) + if err != nil { + return nil, err + } + // oauth2.TokenSource implements the refresh functionality, // we're hijacking it here. - tokenSource := f.config.GetConfig().TokenSource(ctx, token) + tokenSource := authConfig.TokenSource(ctx, token) return tokenSource.Token() } @@ -106,7 +120,7 @@ func (f *grantFilter) refreshTokenIfRequired(c *cookie, ctx filters.FilterContex if c.isAccessTokenExpired() { if canRefresh { - token, err := f.refreshToken(c) + token, err := f.refreshToken(c, ctx.Request()) if err == nil { // Remember that this token was just successfully refreshed // so that we can send an updated cookie in the response. @@ -175,7 +189,7 @@ func (f *grantFilter) Request(ctx filters.FilterContext) { } token, err := f.refreshTokenIfRequired(c, ctx) - if err != nil && c.isAccessTokenExpired() { + if err != nil { // Refresh failed and we no longer have a valid access token. loginRedirect(ctx, f.config) return diff --git a/filters/auth/grant_test.go b/filters/auth/grant_test.go index a485c7c167..78da32d559 100644 --- a/filters/auth/grant_test.go +++ b/filters/auth/grant_test.go @@ -1,12 +1,16 @@ package auth_test import ( + "context" "crypto/tls" "encoding/json" "net" "net/http" + "net/http/cookiejar" "net/http/httptest" "net/url" + "os" + "strings" "testing" "time" @@ -20,6 +24,7 @@ import ( "github.com/zalando/skipper/secrets" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const ( @@ -35,6 +40,42 @@ const ( testQueryParamValue = "param_value" ) +type loggingRoundTripper struct { + http.RoundTripper + t *testing.T +} + +func (rt *loggingRoundTripper) RoundTrip(req *http.Request) (resp *http.Response, err error) { + rt.t.Logf("\n%v", rt.requestString(req)) + + resp, err = rt.RoundTripper.RoundTrip(req) + + if err == nil { + rt.t.Logf("\n%v", rt.responseString(resp)) + } else { + rt.t.Logf("response err: %v", err) + } + return +} + +func (rt *loggingRoundTripper) requestString(req *http.Request) string { + tmp := req.Clone(context.Background()) + tmp.Body = nil + + var b strings.Builder + _ = tmp.Write(&b) + return b.String() +} + +func (rt *loggingRoundTripper) responseString(resp *http.Response) string { + tmp := *resp + tmp.Body = nil + + var b strings.Builder + _ = tmp.Write(&b) + return b.String() +} + func newGrantTestTokeninfo(validToken string, tokenInfoJSON string) *httptest.Server { if tokenInfoJSON == "" { tokenInfoJSON = "{}" @@ -131,6 +172,7 @@ func newGrantTestConfig(tokeninfoURL, providerURL string) *auth.OAuthConfig { ClientID: testClientID, ClientSecret: testClientSecret, Secrets: secrets.NewRegistry(), + SecretsProvider: secrets.NewSecretPaths(1 * time.Hour), SecretFile: testSecretFile, TokeninfoURL: tokeninfoURL, AuthURL: providerURL + "/auth", @@ -843,3 +885,138 @@ func TestGrantTokeninfoKeys(t *testing.T) { assert.JSONEq(t, `{"uid":"bar", "scope":["baz"]}`, rsp.Header.Get("Backend-X-Tokeninfo-Forward")) } + +func TestGrantCredentialsFile(t *testing.T) { + const ( + fooDomain = "foo.skipper.test" + barDomain = "bar.skipper.test" + ) + + dnstest.LoopbackNames(t, fooDomain, barDomain) + + secretsDir := t.TempDir() + + clientIdFile := secretsDir + "/test-client-id" + clientSecretFile := secretsDir + "/test-client-secret" + + require.NoError(t, os.WriteFile(clientIdFile, []byte(testClientID), 0644)) + require.NoError(t, os.WriteFile(clientSecretFile, []byte(testClientSecret), 0644)) + + provider := newGrantTestAuthServer(testToken, testAccessCode) + defer provider.Close() + + tokeninfo := newGrantTestTokeninfo(testToken, "") + defer tokeninfo.Close() + + zero := 0 + config := newGrantTestConfig(tokeninfo.URL, provider.URL) + config.TokenCookieRemoveSubdomains = &zero + config.ClientID = "" + config.ClientSecret = "" + config.ClientIDFile = clientIdFile + config.ClientSecretFile = clientSecretFile + + routes := eskip.MustParse(`* -> oauthGrant() -> status(204) -> `) + + proxy, client := newAuthProxy(t, config, routes, fooDomain, barDomain) + defer proxy.Close() + + // Follow redirects as store cookies + client.CheckRedirect = nil + client.Jar, _ = cookiejar.New(nil) + httpLogger := &loggingRoundTripper{client.Transport, t} + client.Transport = httpLogger + + resetClient := func(t *testing.T) { + client.Jar, _ = cookiejar.New(nil) + httpLogger.t = t + } + + t.Run("request to "+fooDomain+" succeeds", func(t *testing.T) { + resetClient(t) + + rsp, err := client.Get(proxy.URL + "/test") + require.NoError(t, err) + rsp.Body.Close() + + checkStatus(t, rsp, http.StatusNoContent) + }) + + t.Run("request to "+barDomain+" succeeds", func(t *testing.T) { + resetClient(t) + + barUrl := "https://" + net.JoinHostPort(barDomain, proxy.Port) + + rsp, err := client.Get(barUrl + "/test") + require.NoError(t, err) + rsp.Body.Close() + + checkStatus(t, rsp, http.StatusNoContent) + }) +} + +func TestGrantCredentialsPlaceholder(t *testing.T) { + const ( + fooDomain = "foo.skipper.test" + barDomain = "bar.skipper.test" + ) + + dnstest.LoopbackNames(t, fooDomain, barDomain) + + secretsDir := t.TempDir() + + require.NoError(t, os.WriteFile(secretsDir+"/"+fooDomain+"-client-id", []byte(testClientID), 0644)) + require.NoError(t, os.WriteFile(secretsDir+"/"+fooDomain+"-client-secret", []byte(testClientSecret), 0644)) + + provider := newGrantTestAuthServer(testToken, testAccessCode) + defer provider.Close() + + tokeninfo := newGrantTestTokeninfo(testToken, "") + defer tokeninfo.Close() + + zero := 0 + config := newGrantTestConfig(tokeninfo.URL, provider.URL) + config.TokenCookieRemoveSubdomains = &zero + config.ClientID = "" + config.ClientSecret = "" + config.ClientIDFile = secretsDir + "/{host}-client-id" + config.ClientSecretFile = secretsDir + "/{host}-client-secret" + + routes := eskip.MustParse(`* -> oauthGrant() -> status(204) -> `) + + proxy, client := newAuthProxy(t, config, routes, fooDomain, barDomain) + defer proxy.Close() + + // Follow redirects as store cookies + client.CheckRedirect = nil + client.Jar, _ = cookiejar.New(nil) + httpLogger := &loggingRoundTripper{client.Transport, t} + client.Transport = httpLogger + + resetClient := func(t *testing.T) { + client.Jar, _ = cookiejar.New(nil) + httpLogger.t = t + } + + t.Run("request to the hostname with existing client credentials succeeds", func(t *testing.T) { + resetClient(t) + + rsp, err := client.Get(proxy.URL + "/test") + require.NoError(t, err) + rsp.Body.Close() + + checkStatus(t, rsp, http.StatusNoContent) + }) + + t.Run("request to the hostname without existing client credentials is forbidden", func(t *testing.T) { + resetClient(t) + + barUrl := "https://" + net.JoinHostPort(barDomain, proxy.Port) + + rsp, err := client.Get(barUrl + "/test") + require.NoError(t, err) + rsp.Body.Close() + + checkStatus(t, rsp, http.StatusForbidden) + }) +} diff --git a/filters/auth/grantcallback.go b/filters/auth/grantcallback.go index 5e3ebb5c61..f95469d73d 100644 --- a/filters/auth/grantcallback.go +++ b/filters/auth/grantcallback.go @@ -29,13 +29,18 @@ func (s *grantCallbackSpec) CreateFilter([]interface{}) (filters.Filter, error) }, nil } -func (f *grantCallbackFilter) exchangeAccessToken(code string, redirectURI string) (*oauth2.Token, error) { +func (f *grantCallbackFilter) exchangeAccessToken(req *http.Request, code string) (*oauth2.Token, error) { + authConfig, err := f.config.GetConfig(req) + if err != nil { + return nil, err + } + redirectURI, _ := f.config.RedirectURLs(req) ctx := providerContext(f.config) params := f.config.GetAuthURLParameters(redirectURI) - return f.config.GetConfig().Exchange(ctx, code, params...) + return authConfig.Exchange(ctx, code, params...) } -func (f *grantCallbackFilter) loginCallback(ctx filters.FilterContext) { +func (f *grantCallbackFilter) Request(ctx filters.FilterContext) { req := ctx.Request() q := req.URL.Query() @@ -79,8 +84,7 @@ func (f *grantCallbackFilter) loginCallback(ctx filters.FilterContext) { return } - redirectURI, _ := f.config.RedirectURLs(req) - token, err := f.exchangeAccessToken(code, redirectURI) + token, err := f.exchangeAccessToken(req, code) if err != nil { log.Errorf("Failed to exchange access token: %v.", err) serverError(ctx) @@ -103,8 +107,4 @@ func (f *grantCallbackFilter) loginCallback(ctx filters.FilterContext) { }) } -func (f *grantCallbackFilter) Request(ctx filters.FilterContext) { - f.loginCallback(ctx) -} - func (f *grantCallbackFilter) Response(ctx filters.FilterContext) {} diff --git a/filters/auth/grantconfig.go b/filters/auth/grantconfig.go index 3cb4528f57..04b164eeb6 100644 --- a/filters/auth/grantconfig.go +++ b/filters/auth/grantconfig.go @@ -3,22 +3,26 @@ package auth import ( "errors" "fmt" + "net" "net/http" + "path/filepath" + "strings" "time" "github.com/opentracing/opentracing-go" "github.com/zalando/skipper/filters" - "github.com/zalando/skipper/net" + snet "github.com/zalando/skipper/net" "github.com/zalando/skipper/routing" "github.com/zalando/skipper/secrets" "golang.org/x/oauth2" ) type OAuthConfig struct { - initialized bool - flowState *flowState - + initialized bool + flowState *flowState grantTokeninfoKeysLookup map[string]struct{} + getClientId func(*http.Request) (string, error) + getClientSecret func(*http.Request) (string, error) // TokeninfoURL is the URL of the service to validate OAuth2 tokens. TokeninfoURL string @@ -55,13 +59,17 @@ type OAuthConfig struct { ClientSecret string // ClientIDFile, the path to the file containing the OAuth2 client id of - // the current service, used to exchange the access code. Must be set if - // ClientID is not provided. Requires SecretsProvider and will be added to it. + // the current service, used to exchange the access code. + // Must be set if ClientID is not provided. + // File name may contain {host} placeholder which will be replaced by the request host. + // Requires SecretsProvider, the path (or path's directory if placeholder is present) will be added to it. ClientIDFile string // ClientSecretFile, the path to the file containing the secret associated - // with the ClientID, used to exchange the access code. Must be set if - // ClientSecret is not provided. Requires SecretsProvider and will be added to it. + // with the ClientID, used to exchange the access code. + // Must be set if ClientSecret is not provided. + // File name may contain {host} placeholder which will be replaced by the request host. + // Requires SecretsProvider, the path (or path's directory if placeholder is present) will be added to it. ClientSecretFile string // SecretsProvider is used to read ClientIDFile and ClientSecretFile from the @@ -76,7 +84,7 @@ type OAuthConfig struct { // AuthClient, optional. When set, it will be used for the // access code exchange requests to TokenURL. When not set, a new default // client is created. - AuthClient *net.Client + AuthClient *snet.Client // AuthURLParameters, optional. Extra URL parameters to add when calling // the OAuth2 authorize or token endpoints. @@ -143,14 +151,6 @@ func (c *OAuthConfig) Init() error { return ErrMissingSecretFile } - if c.ClientID == "" && c.ClientIDFile == "" { - return ErrMissingClientID - } - - if c.ClientSecret == "" && c.ClientSecretFile == "" { - return ErrMissingClientSecret - } - if c.CallbackPath == "" { c.CallbackPath = defaultCallbackPath } @@ -181,7 +181,7 @@ func (c *OAuthConfig) Init() error { } if c.AuthClient == nil { - c.AuthClient = net.NewClient(net.Options{ + c.AuthClient = snet.NewClient(snet.Options{ ResponseHeaderTimeout: c.ConnectionTimeout, TLSHandshakeTimeout: c.ConnectionTimeout, MaxIdleConnsPerHost: c.MaxIdleConnectionsPerHost, @@ -193,22 +193,58 @@ func (c *OAuthConfig) Init() error { c.flowState = newFlowState(c.Secrets, c.SecretFile) - if c.ClientIDFile != "" { + if c.ClientID != "" { + c.getClientId = func(*http.Request) (string, error) { + return c.ClientID, nil + } + } else if c.ClientIDFile != "" { if c.SecretsProvider == nil { return ErrMissingSecretsProvider } - if err := c.SecretsProvider.Add(c.ClientIDFile); err != nil { - return err + if hasPlaceholders(c.ClientIDFile) { + c.getClientId = func(req *http.Request) (string, error) { + return c.getSecret(resolvePlaceholders(c.ClientIDFile, req)) + } + if err := c.SecretsProvider.Add(filepath.Dir(c.ClientIDFile)); err != nil { + return err + } + } else { + c.getClientId = func(*http.Request) (string, error) { + return c.getSecret(c.ClientIDFile) + } + if err := c.SecretsProvider.Add(c.ClientIDFile); err != nil { + return err + } } + } else { + return ErrMissingClientID } - if c.ClientSecretFile != "" { + if c.ClientSecret != "" { + c.getClientSecret = func(*http.Request) (string, error) { + return c.ClientSecret, nil + } + } else if c.ClientSecretFile != "" { if c.SecretsProvider == nil { return ErrMissingSecretsProvider } - if err := c.SecretsProvider.Add(c.ClientSecretFile); err != nil { - return err + if hasPlaceholders(c.ClientSecretFile) { + c.getClientSecret = func(req *http.Request) (string, error) { + return c.getSecret(resolvePlaceholders(c.ClientSecretFile, req)) + } + if err := c.SecretsProvider.Add(filepath.Dir(c.ClientSecretFile)); err != nil { + return err + } + } else { + c.getClientSecret = func(*http.Request) (string, error) { + return c.getSecret(c.ClientSecretFile) + } + if err := c.SecretsProvider.Add(c.ClientSecretFile); err != nil { + return err + } } + } else { + return ErrMissingClientSecret } if len(c.GrantTokeninfoKeys) > 0 { @@ -246,51 +282,58 @@ func (c *OAuthConfig) NewGrantPreprocessor() routing.PreProcessor { return &grantPrep{config: c} } -func (c *OAuthConfig) GetConfig() *oauth2.Config { - return &oauth2.Config{ +func (c *OAuthConfig) GetConfig(req *http.Request) (*oauth2.Config, error) { + var err error + authConfig := &oauth2.Config{ Endpoint: oauth2.Endpoint{ AuthURL: c.AuthURL, TokenURL: c.TokenURL, }, - ClientID: c.GetClientID(), - ClientSecret: c.GetClientSecret(), } -} -func (c *OAuthConfig) GetAuthURLParameters(redirectURI string) []oauth2.AuthCodeOption { - params := []oauth2.AuthCodeOption{oauth2.SetAuthURLParam("redirect_uri", redirectURI)} + authConfig.ClientID, err = c.getClientId(req) + if err != nil { + return nil, err + } - if c.AuthURLParameters != nil { - for k, v := range c.AuthURLParameters { - params = append(params, oauth2.SetAuthURLParam(k, v)) - } + authConfig.ClientSecret, err = c.getClientSecret(req) + if err != nil { + return nil, err } - return params + return authConfig, nil } -func (c *OAuthConfig) GetClientID() string { - if c.ClientID != "" { - return c.ClientID +func (c *OAuthConfig) getSecret(file string) (string, error) { + if secret, ok := c.SecretsProvider.GetSecret(file); ok { + return string(secret), nil + } else { + return "", fmt.Errorf("secret %s does not exist", file) } +} - if id, ok := c.SecretsProvider.GetSecret(c.ClientIDFile); ok { - return string(id) +func resolvePlaceholders(s string, r *http.Request) string { + h, _, err := net.SplitHostPort(r.Host) + if err != nil { + h = r.Host } + return strings.ReplaceAll(s, "{host}", h) +} - return "" +func hasPlaceholders(s string) bool { + return resolvePlaceholders(s, &http.Request{Host: "example.org"}) != s } -func (c *OAuthConfig) GetClientSecret() string { - if c.ClientSecret != "" { - return c.ClientSecret - } +func (c *OAuthConfig) GetAuthURLParameters(redirectURI string) []oauth2.AuthCodeOption { + params := []oauth2.AuthCodeOption{oauth2.SetAuthURLParam("redirect_uri", redirectURI)} - if secret, ok := c.SecretsProvider.GetSecret(c.ClientSecretFile); ok { - return string(secret) + if c.AuthURLParameters != nil { + for k, v := range c.AuthURLParameters { + params = append(params, oauth2.SetAuthURLParam(k, v)) + } } - return "" + return params } // RedirectURLs constructs the redirect URI based on the request and the diff --git a/filters/auth/grantlogout.go b/filters/auth/grantlogout.go index 5444fdd033..7ba53c501a 100644 --- a/filters/auth/grantlogout.go +++ b/filters/auth/grantlogout.go @@ -2,7 +2,6 @@ package auth import ( "encoding/json" - "errors" "fmt" "io" "net/http" @@ -11,6 +10,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/zalando/skipper/filters" + "golang.org/x/oauth2" ) const ( @@ -45,20 +45,6 @@ func (s *grantLogoutSpec) CreateFilter([]interface{}) (filters.Filter, error) { }, nil } -func (f *grantLogoutFilter) getBasicAuthCredentials() (string, string, error) { - clientID := f.config.GetClientID() - if clientID == "" { - return "", "", errors.New("failed to create token revoke auth header: no client ID") - } - - clientSecret := f.config.GetClientSecret() - if clientSecret == "" { - return "", "", errors.New("failed to create token revoke auth header: no client secret") - } - - return clientID, clientSecret, nil -} - func responseToError(responseData []byte, statusCode int, tokenType string) error { var errorResponse revokeErrorResponse err := json.Unmarshal(responseData, &errorResponse) @@ -81,7 +67,7 @@ func responseToError(responseData []byte, statusCode int, tokenType string) erro ) } -func (f *grantLogoutFilter) revokeTokenType(tokenType string, token string) error { +func (f *grantLogoutFilter) revokeTokenType(c *oauth2.Config, tokenType string, token string) error { revokeURL, err := url.Parse(f.config.RevokeTokenURL) if err != nil { return err @@ -106,12 +92,7 @@ func (f *grantLogoutFilter) revokeTokenType(tokenType string, token string) erro return err } - clientId, clientSecret, err := f.getBasicAuthCredentials() - if err != nil { - return err - } - - revokeRequest.SetBasicAuth(clientId, clientSecret) + revokeRequest.SetBasicAuth(c.ClientID, c.ClientSecret) revokeRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded") revokeResponse, err := f.config.AuthClient.Do(revokeRequest) @@ -166,16 +147,22 @@ func (f *grantLogoutFilter) Request(ctx filters.FilterContext) { return } + authConfig, err := f.config.GetConfig(req) + if err != nil { + serverError(ctx) + return + } + var accessTokenRevokeError, refreshTokenRevokeError error if c.AccessToken != "" { - accessTokenRevokeError = f.revokeTokenType(accessTokenType, c.AccessToken) + accessTokenRevokeError = f.revokeTokenType(authConfig, accessTokenType, c.AccessToken) if accessTokenRevokeError != nil { log.Error(accessTokenRevokeError) } } if c.RefreshToken != "" { - refreshTokenRevokeError = f.revokeTokenType(refreshTokenType, c.RefreshToken) + refreshTokenRevokeError = f.revokeTokenType(authConfig, refreshTokenType, c.RefreshToken) if refreshTokenRevokeError != nil { log.Error(refreshTokenRevokeError) } diff --git a/secrets/file.go b/secrets/file.go index 54d60b0fb3..0a5e2e549e 100644 --- a/secrets/file.go +++ b/secrets/file.go @@ -17,7 +17,6 @@ const ( ) var ( - ErrAlreadyExists = errors.New("secret already exists") ErrWrongFileType = errors.New("file type not supported") ErrFailedToReadFile = errors.New("failed to read file") ) @@ -133,7 +132,7 @@ func (sp *SecretPaths) handleDir(p string) error { func (sp *SecretPaths) registerSecretFile(p string) error { if _, ok := sp.GetSecret(p); ok { - return ErrAlreadyExists + return nil } dat, err := os.ReadFile(p) if err != nil { diff --git a/skipper.go b/skipper.go index 10f482f6d9..346ac4aba9 100644 --- a/skipper.go +++ b/skipper.go @@ -759,10 +759,12 @@ type Options struct { // OAuth2ClientIDFile, the path of the file containing the OAuth2 client id of // the current service, used to exchange the access code. + // File name may contain {host} placeholder which will be replaced by the request host. OAuth2ClientIDFile string // OAuth2ClientSecretFile, the path of the file containing the secret associated // with the ClientID, used to exchange the access code. + // File name may contain {host} placeholder which will be replaced by the request host. OAuth2ClientSecretFile string // OAuth2CallbackPath contains the path where the OAuth2 callback requests with the