GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — feat: Expose the OIDC JWKS tunables through the operator by larrysingleton007 · Pull Request #6690 · feast-dev/feast

feat: Expose the OIDC JWKS tunables through the operator #6690

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Sign up for GitHub

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
ntkathole merged 3 commits into feast-dev:master from larrysingleton007:feat/operator-jwks-tunables
Aug 14, 2026

Uh oh!

There was an error while loading. Please reload this page.

Merged

feat: Expose the OIDC JWKS tunables through the operator #6690

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
3 commits
Select commit Hold shift + click to select a range
183472e
feat: Expose the OIDC JWKS tunables through the operator
larrysingleton007 Jul 31, 2026
c8fdd65
fix: Regenerate the OLM bundle and close review gaps
larrysingleton007 Jul 31, 2026
b2609bb
docs: State that the OIDC authz options require apiVersion v1
larrysingleton007 Aug 3, 2026
File filter
Filter by extension

Clear filters
Conversations
Failed to load comments.
Loading

Uh oh!

There was an error while loading. Please reload this page.

Jump to
Jump to file
Failed to load files.
Loading

Uh oh!

There was an error while loading. Please reload this page.

Diff view
Diff view

There are no files selected for viewing

8 changes: 4 additions & 4 deletions 8 .secrets.baseline
Show comments
View file
Open in desktop

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Oops, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

20 changes: 19 additions & 1 deletion 20 docs/how-to-guides/feast-operator/05-security.md
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ spec:

### Advanced OIDC options

{% hint style="warning" %}
Every option in this section requires `apiVersion: feast.dev/v1`. Under the deprecated
`feast.dev/v1alpha1`, `authz.oidc` accepts only `secretRef`. The CRD has no conversion
webhook, so a resource submitted as v1alpha1 is validated against the v1alpha1 schema and
any other field is pruned without error rather than rejected. Applying the example below
as v1alpha1 therefore leaves OIDC configured by Secret alone, with none of these settings
taking effect and nothing in the output to say so. Use v1, which is the storage version.
{% endhint %}

```yaml
authz:
oidc:
Expand All @@ -101,9 +110,18 @@ authz:
secretKeyName: client_id # override the default Secret key name
tokenEnvVar: FEAST_TOKEN # env var from which servers read the Bearer token
verifySSL: false # disable SSL verification (dev only)
caCertConfigMap: oidc-ca-cert # ConfigMap with CA cert for SSL verification
caCertConfigMap: # ConfigMap with CA cert for SSL verification
name: oidc-ca-cert
jwksCacheLifespanSeconds: 300 # how long servers reuse the fetched JWK set
jwksRequestTimeoutSeconds: 10 # network timeout for the JWKS fetch
```

`jwksCacheLifespanSeconds` is not only a performance setting: it also bounds how long a key the provider has **revoked** continues to validate tokens. Lower it if your provider rotates or revokes aggressively, at the cost of proportionally more JWKS fetches. Key rotations that introduce a new key id are picked up immediately regardless, because an unknown key id forces a refetch. `jwksRequestTimeoutSeconds` bounds how long an unresponsive provider can block request serving. Both must be at least 1. When unset, neither key is written to the generated configuration and the feature server applies its own defaults (300 and 10 seconds respectively).

{% hint style="warning" %}
These two options require a feature server image that recognizes them. The operator deploys a matching image by default, so this only applies if you pin an older one explicitly, through a container `image` override or the operator's `RELATED_IMAGE_FEATURE_SERVER` setting. An image that predates these options rejects its configuration at startup, so leave them unset until the pinned image is updated.
{% endhint %}

**SDK docs**: [Feast OIDC Auth](../../getting-started/components/authz_manager.md#oidc-authorization)

---
Expand Down
11 changes: 11 additions & 0 deletions 11 infra/feast-operator/api/v1/featurestore_types.go
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,17 @@ type OidcAuthz struct {
// ConfigMap with the CA certificate for self-signed OIDC providers. Auto-detected on RHOAI/ODH.
// +optional
CACertConfigMap *OidcCACertConfigMap `json:"caCertConfigMap,omitempty"`
// Seconds the servers reuse the provider's fetched JWK set before refetching. Defaults to 300.
// Also bounds how long a key the provider revoked keeps validating tokens, so lower it if the
// provider rotates or revokes aggressively, at the cost of more JWKS fetches.
// +optional
// +kubebuilder:validation:Minimum=1
JwksCacheLifespanSeconds *int32 `json:"jwksCacheLifespanSeconds,omitempty"`
// Seconds before a JWKS fetch times out. Defaults to 10. The fetch happens inline on the request
// path, so an unresponsive provider blocks serving for at most this long.
// +optional
// +kubebuilder:validation:Minimum=1
JwksRequestTimeoutSeconds *int32 `json:"jwksRequestTimeoutSeconds,omitempty"`
}

// OidcCACertConfigMap references a ConfigMap containing a CA certificate for OIDC provider TLS.
Expand Down
10 changes: 10 additions & 0 deletions 10 infra/feast-operator/api/v1/zz_generated.deepcopy.go
Show comments
View file
Open in desktop

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Oops, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

2 changes: 1 addition & 1 deletion 2 infra/feast-operator/bundle/manifests/feast-operator.clusterserviceversion.yaml
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ metadata:
}
]
capabilities: Basic Install
createdAt: "2026-07-20T13:27:58Z"
createdAt: "2026-07-31T18:28:04Z"
operators.operatorframework.io/builder: operator-sdk-v1.41.0
operators.operatorframework.io/project_layout: go.kubebuilder.io/v4
name: feast-operator.v0.65.0
Expand Down
24 changes: 24 additions & 0 deletions 24 infra/feast-operator/bundle/manifests/feast.dev_featurestores.yaml
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,18 @@ spec:
to derive the discovery endpoint.
pattern: ^https://\S+$
type: string
jwksCacheLifespanSeconds:
description: Seconds the servers reuse the provider's fetched
JWK set before refetching. Defaults to 300.
format: int32
minimum: 1
type: integer
jwksRequestTimeoutSeconds:
description: Seconds before a JWKS fetch times out. Defaults
to 10.
format: int32
minimum: 1
type: integer
secretKeyName:
description: Key in the Secret containing all OIDC properties
as a YAML value. If unset, each key is a property.
Expand Down Expand Up @@ -6390,6 +6402,18 @@ spec:
to derive the discovery endpoint.
pattern: ^https://\S+$
type: string
jwksCacheLifespanSeconds:
description: Seconds the servers reuse the provider's
fetched JWK set before refetching. Defaults to 300.
format: int32
minimum: 1
type: integer
jwksRequestTimeoutSeconds:
description: Seconds before a JWKS fetch times out. Defaults
to 10.
format: int32
minimum: 1
type: integer
secretKeyName:
description: Key in the Secret containing all OIDC properties
as a YAML value. If unset, each key is a property.
Expand Down
24 changes: 24 additions & 0 deletions 24 infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,18 @@ spec:
to derive the discovery endpoint.
pattern: ^https://\S+$
type: string
jwksCacheLifespanSeconds:
description: Seconds the servers reuse the provider's fetched
JWK set before refetching. Defaults to 300.
format: int32
minimum: 1
type: integer
jwksRequestTimeoutSeconds:
description: Seconds before a JWKS fetch times out. Defaults
to 10.
format: int32
minimum: 1
type: integer
secretKeyName:
description: Key in the Secret containing all OIDC properties
as a YAML value. If unset, each key is a property.
Expand Down Expand Up @@ -6390,6 +6402,18 @@ spec:
to derive the discovery endpoint.
pattern: ^https://\S+$
type: string
jwksCacheLifespanSeconds:
description: Seconds the servers reuse the provider's
fetched JWK set before refetching. Defaults to 300.
format: int32
minimum: 1
type: integer
jwksRequestTimeoutSeconds:
description: Seconds before a JWKS fetch times out. Defaults
to 10.
format: int32
minimum: 1
type: integer
secretKeyName:
description: Key in the Secret containing all OIDC properties
as a YAML value. If unset, each key is a property.
Expand Down
24 changes: 24 additions & 0 deletions 24 infra/feast-operator/dist/install.yaml
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,18 @@ spec:
to derive the discovery endpoint.
pattern: ^https://\S+$
type: string
jwksCacheLifespanSeconds:
description: Seconds the servers reuse the provider's fetched
JWK set before refetching. Defaults to 300.
format: int32
minimum: 1
type: integer
jwksRequestTimeoutSeconds:
description: Seconds before a JWKS fetch times out. Defaults
to 10.
format: int32
minimum: 1
type: integer
secretKeyName:
description: Key in the Secret containing all OIDC properties
as a YAML value. If unset, each key is a property.
Expand Down Expand Up @@ -6398,6 +6410,18 @@ spec:
to derive the discovery endpoint.
pattern: ^https://\S+$
type: string
jwksCacheLifespanSeconds:
description: Seconds the servers reuse the provider's
fetched JWK set before refetching. Defaults to 300.
format: int32
minimum: 1
type: integer
jwksRequestTimeoutSeconds:
description: Seconds before a JWKS fetch times out. Defaults
to 10.
format: int32
minimum: 1
type: integer
secretKeyName:
description: Key in the Secret containing all OIDC properties
as a YAML value. If unset, each key is a property.
Expand Down
5 changes: 5 additions & 0 deletions 5 infra/feast-operator/docs/api/markdown/ref.md
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,11 @@ _Appears in:_
| `tokenEnvVar` _string_ | Env var name for client pods to read an OIDC token from. Sets token_env_var in client config. |
| `verifySSL` _boolean_ | Verify SSL certificates for the OIDC provider. Defaults to true. |
| `caCertConfigMap` _[OidcCACertConfigMap](#oidccacertconfigmap)_ | ConfigMap with the CA certificate for self-signed OIDC providers. Auto-detected on RHOAI/ODH. |
| `jwksCacheLifespanSeconds` _integer_ | Seconds the servers reuse the provider's fetched JWK set before refetching. Defaults to 300.
Also bounds how long a key the provider revoked keeps validating tokens, so lower it if the
provider rotates or revokes aggressively, at the cost of more JWKS fetches. |
| `jwksRequestTimeoutSeconds` _integer_ | Seconds before a JWKS fetch times out. Defaults to 10. The fetch happens inline on the request
path, so an unresponsive provider blocks serving for at most this long. |


#### OidcCACertConfigMap
Expand Down
6 changes: 6 additions & 0 deletions 6 infra/feast-operator/internal/controller/services/repo_config.go
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ func getBaseServiceRepoConfig(
if oidcAuthz.VerifySSL != nil {
oidcParameters[string(OidcVerifySsl)] = *oidcAuthz.VerifySSL
}
if oidcAuthz.JwksCacheLifespanSeconds != nil {
oidcParameters[string(OidcJwksCacheLifespanSeconds)] = *oidcAuthz.JwksCacheLifespanSeconds
}
if oidcAuthz.JwksRequestTimeoutSeconds != nil {
oidcParameters[string(OidcJwksRequestTimeoutSeconds)] = *oidcAuthz.JwksRequestTimeoutSeconds
}
if caCertPath := resolveOidcCACertPath(oidcAuthz, odhCaBundleExists); caCertPath != "" {
oidcParameters[string(OidcCaCertPath)] = caCertPath
}
Expand Down
26 changes: 26 additions & 0 deletions 26 infra/feast-operator/internal/controller/services/repo_config_test.go
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,32 @@ var _ = Describe("Repo Config", func() {
Expect(repoConfig.AuthzConfig.OidcParameters).To(HaveLen(1))
Expect(repoConfig.AuthzConfig.OidcParameters[string(OidcAuthDiscoveryUrl)]).To(Equal("https://keycloak.example.com/realms/test/.well-known/openid-configuration"))

By("Omitting the JWKS tunables when unset, so the SDK defaults apply")
Expect(repoConfig.AuthzConfig.OidcParameters).NotTo(HaveKey(string(OidcJwksCacheLifespanSeconds)))
Expect(repoConfig.AuthzConfig.OidcParameters).NotTo(HaveKey(string(OidcJwksRequestTimeoutSeconds)))

By("Forwarding the JWKS tunables when set on the CR")
jwksLifespan := int32(60)
jwksTimeout := int32(5)
featureStore.Spec.AuthzConfig = &feastdevv1.AuthzConfig{
OidcAuthz: &feastdevv1.OidcAuthz{
IssuerUrl: "https://keycloak.example.com/realms/test",
JwksCacheLifespanSeconds: &jwksLifespan,
JwksRequestTimeoutSeconds: &jwksTimeout,
},
}
ApplyDefaultsToStatus(featureStore)
repoConfig, err = getServiceRepoConfig(featureStore, emptyMockExtractConfigFromSecret, emptyMockExtractConfigFromConfigMap, false)
Expect(err).NotTo(HaveOccurred())
Expect(repoConfig.AuthzConfig.OidcParameters).To(HaveLen(3))
Expect(repoConfig.AuthzConfig.OidcParameters).To(HaveKeyWithValue(string(OidcJwksCacheLifespanSeconds), int32(60)))
Expect(repoConfig.AuthzConfig.OidcParameters).To(HaveKeyWithValue(string(OidcJwksRequestTimeoutSeconds), int32(5)))

By("Keeping the JWKS tunables out of the client config, which does not accept them")
clientRepoConfig := getClientRepoConfig(featureStore, nil)
Expect(clientRepoConfig.AuthzConfig.OidcParameters).NotTo(HaveKey(string(OidcJwksCacheLifespanSeconds)))
Expect(clientRepoConfig.AuthzConfig.OidcParameters).NotTo(HaveKey(string(OidcJwksRequestTimeoutSeconds)))

By("Having oidc with issuerUrl on CR and auth_discovery_url in Secret — CR wins")
featureStore.Spec.AuthzConfig = &feastdevv1.AuthzConfig{
OidcAuthz: &feastdevv1.OidcAuthz{
Expand Down
3 changes: 3 additions & 0 deletions 3 infra/feast-operator/internal/controller/services/services_types.go
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ const (
OidcAudience OidcPropertyType = "audience"
OidcIssuer OidcPropertyType = "issuer"

OidcJwksCacheLifespanSeconds OidcPropertyType = "jwks_cache_lifespan_seconds"
OidcJwksRequestTimeoutSeconds OidcPropertyType = "jwks_request_timeout_seconds"

OidcMissingSecretError string = "missing OIDC secret: %s"

// Common string constants
Expand Down
15 changes: 15 additions & 0 deletions 15 infra/feast-operator/test/api/featurestore_types_test.go
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,13 @@ func authzConfigWithOidc(featureStore *feastdevv1.FeatureStore) *feastdevv1.Feat
return fsCopy
}

func authzConfigWithOidcJwksTunables(jwksCacheLifespanSeconds *int32, jwksRequestTimeoutSeconds *int32, featureStore *feastdevv1.FeatureStore) *feastdevv1.FeatureStore {
fsCopy := authzConfigWithOidc(featureStore)
fsCopy.Spec.AuthzConfig.OidcAuthz.JwksCacheLifespanSeconds = jwksCacheLifespanSeconds
fsCopy.Spec.AuthzConfig.OidcAuthz.JwksRequestTimeoutSeconds = jwksRequestTimeoutSeconds
return fsCopy
}

func onlineStoreWithDBPersistenceType(dbPersistenceType string, featureStore *feastdevv1.FeatureStore) *feastdevv1.FeatureStore {
fsCopy := featureStore.DeepCopy()
fsCopy.Spec.Services = &feastdevv1.FeatureStoreServices{
Expand Down Expand Up @@ -610,6 +617,14 @@ var _ = Describe("FeatureStore API", func() {
It("should fail when both kubernetes and oidc settings are given", func() {
attemptInvalidCreationAndAsserts(ctx, authzConfigWithOidc(authzConfigWithKubernetes(featurestore)), "One selection required between kubernetes or oidc")
})
It("should fail when the oidc JWKS cache lifespan is below one second", func() {
zero := int32(0)
attemptInvalidCreationAndAsserts(ctx, authzConfigWithOidcJwksTunables(&zero, nil, featurestore), "should be greater than or equal to 1")
})
It("should fail when the oidc JWKS request timeout is below one second", func() {
zero := int32(0)
attemptInvalidCreationAndAsserts(ctx, authzConfigWithOidcJwksTunables(nil, &zero, featurestore), "should be greater than or equal to 1")
})
})

Context("When creating a Registry", func() {
Expand Down
Loading
Oops, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Add this suggestion to a batch that can be applied as a single commit. This suggestion is invalid because no changes were made to the code. Suggestions cannot be applied while the pull request is closed. Suggestions cannot be applied while viewing a subset of changes. Only one suggestion per line can be applied in a batch. Add this suggestion to a batch that can be applied as a single commit. Applying suggestions on deleted lines is not supported. You must change the existing code in this line in order to create a valid suggestion. Outdated suggestions cannot be applied. This suggestion has been applied or marked resolved. Suggestions cannot be applied from pending reviews. Suggestions cannot be applied on multi-line comments. Suggestions cannot be applied while the pull request is queued to merge. Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.