Allow to configure the JWKS refresh settings

This exposes a couple for knobs for the jwks keyfunc module to adjust
timeout and refresh intervals.
This commit is contained in:
Ralf Haferkamp
2022-07-22 15:01:25 +02:00
committed by Ralf Haferkamp
parent eb94530433
commit 8229567213
6 changed files with 31 additions and 5 deletions

View File

@@ -190,6 +190,7 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config)
middleware.TokenCacheSize(cfg.OIDC.UserinfoCache.Size),
middleware.TokenCacheTTL(time.Second*time.Duration(cfg.OIDC.UserinfoCache.TTL)),
middleware.AccessTokenVerifyMethod(cfg.OIDC.AccessTokenVerifyMethod),
middleware.JWKSOptions(cfg.OIDC.JWKS),
// basic Options
middleware.Logger(logger),

View File

@@ -94,6 +94,14 @@ type OIDC struct {
Insecure bool `yaml:"insecure" env:"OCIS_INSECURE;PROXY_OIDC_INSECURE" desc:"Disable TLS certificate validation for connections to the IDP. Note that this is not recommended for production environments."`
AccessTokenVerifyMethod string `yaml:"access_token_verify_method" env:"PROXY_OIDC_ACCESS_TOKEN_VERIFY_METHOD" desc:"Sets how OIDC access tokens should be verified. Possible values: 'none', which means that no special validation apart from using it for accessing the IPD's userinfo endpoint will be done. Or 'jwt', which tries to parse the access token as a jwt token and verifies the signature using the keys published on the IDP's 'jwks_uri'."`
UserinfoCache UserinfoCache `yaml:"user_info_cache"`
JWKS JWKS `yaml:"jwks"`
}
type JWKS struct {
RefreshInterval uint64 `yaml:"refresh_interval" env:"PROXY_OIDC_JWKS_REFRESH_INTERVAL" desc:"The interval for refreshing the JWKS in the background via a new HTTP request to the IDP in minutes."`
RefreshTimeout uint64 `yaml:"refresh_timeout" env:"PROXY_OIDC_JWKS_REFRESH_TIMEOUT" desc:"The timeout, in seconds, for and outgoing JWKS request."`
RefreshRateLimit uint64 `yaml:"refresh_limit" env:"PROXY_OIDC_JWKS_REFRESH_RATE_LIMIT" desc:"Limits the rate at which refresh requests are performed for unknown keys in seconds. (To prevent malicious client from imposing high network load on the IDP via ocis)"`
RefreshUnknownKID bool `yaml:"refresh_unknown_kid" env:"PROXY_OIDC_JWKS_REFRESH_UNKNOWN_KID" desc:"If true the JWKS refresh request will occur every time an unknown key id (kid) is seen. Always set a 'refresh_limit' when enabling this"`
}
// UserinfoCache is a TTL cache configuration.

View File

@@ -41,6 +41,12 @@ func DefaultConfig() *config.Config {
Size: 1024,
TTL: 10,
},
JWKS: config.JWKS{
RefreshInterval: 60, // minutes
RefreshRateLimit: 60, // seconds
RefreshTimeout: 10, // seconds
RefreshUnknownKID: true,
},
},
PolicySelector: nil,
Reva: &config.Reva{

View File

@@ -116,6 +116,7 @@ func newOIDCAuth(options Options) func(http.Handler) http.Handler {
TokenCacheTTL(options.UserinfoCacheTTL),
CredentialsByUserAgent(options.CredentialsByUserAgent),
AccessTokenVerifyMethod(options.AccessTokenVerifyMethod),
JWKSOptions(options.JWKS),
)
}

View File

@@ -37,6 +37,7 @@ func OIDCAuth(optionSetters ...Option) func(next http.Handler) http.Handler {
tokenCache: &tokenCache,
tokenCacheTTL: options.UserinfoCacheTTL,
accessTokenVerifyMethod: options.AccessTokenVerifyMethod,
jwksOptions: options.JWKS,
}
return func(next http.Handler) http.Handler {
@@ -77,6 +78,7 @@ func OIDCAuth(optionSetters ...Option) func(next http.Handler) http.Handler {
type oidcAuth struct {
logger log.Logger
provider OIDCProvider
jwksOptions config.JWKS
jwks *keyfunc.JWKS
providerFunc func() (OIDCProvider, error)
httpClient *http.Client
@@ -233,16 +235,15 @@ func (m *oidcAuth) getKeyfunc() *keyfunc.JWKS {
return nil
}
m.logger.Debug().Str("jwks", j.JWKSURL).Msg("discovered jwks endpoint")
// FIXME: make configurable
options := keyfunc.Options{
Client: m.httpClient,
RefreshErrorHandler: func(err error) {
m.logger.Error().Err(err).Msg("There was an error with the jwt.Keyfunc")
},
RefreshInterval: time.Hour,
RefreshRateLimit: time.Minute * 5,
RefreshTimeout: time.Second * 10,
RefreshUnknownKID: true,
RefreshInterval: time.Minute * time.Duration(m.jwksOptions.RefreshInterval),
RefreshRateLimit: time.Second * time.Duration(m.jwksOptions.RefreshRateLimit),
RefreshTimeout: time.Second * time.Duration(m.jwksOptions.RefreshTimeout),
RefreshUnknownKID: m.jwksOptions.RefreshUnknownKID,
}
m.jwks, err = keyfunc.Get(j.JWKSURL, options)
if err != nil {

View File

@@ -58,6 +58,8 @@ type Options struct {
// AccessTokenVerifyMethod configures how access_tokens should be verified but the oidc_auth middleware.
// Possible values currently: "jwt" and "none"
AccessTokenVerifyMethod string
// JWKS sets the options for fetching the JWKS from the IDP
JWKS config.JWKS
}
// newOptions initializes the available default options.
@@ -203,3 +205,10 @@ func AccessTokenVerifyMethod(method string) Option {
o.AccessTokenVerifyMethod = method
}
}
// JWKS sets the options for fetching the JWKS from the IDP
func JWKSOptions(jo config.JWKS) Option {
return func(o *Options) {
o.JWKS = jo
}
}