GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — fix: Reuse IdP-issued client tokens until near expiry by larrysingleton007 · Pull Request #6687 · feast-dev/feast Conversation
Contributor
What this PR does / why we need itThis is the client-side sibling of #6683. On the
So each request pays a discovery GET plus a token POST against the IdP for a token that is typically valid for an hour. Measured with counting mocks on master: 5 outbound calls produce 5 discovery GETs and 5 token POSTs. Beyond the added latency, a batch loop multiplies IdP load by request count, which can trip provider rate limits. The fix caches issued tokens keyed by the token-request identity (discovery URL, client id and secret, username, password) and reuses each until shortly before expiry, taken from the token's own The cache is module level because the interceptors construct a fresh manager per RPC, so instance state cannot survive between calls. Only the A few details worth reviewer attention:
One interaction worth flagging: Testing: 7 new unit tests covering reuse until expiry, refusal to cache inside the refresh margin, the Which issue(s) this PR fixesFixes #6684
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page. All reactions
|
There was an error while loading. Please reload this page.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@ ## master #6687 +/- ## ========================================== - Coverage 46.92% 46.91% -0.01% ========================================== Files 416 416 Lines 50549 50545 -4 Branches 7252 7252 ========================================== - Hits 23718 23714 -4 Misses 25157 25157 Partials 1674 1674
*This pull request uses carry forward flags. Click here to find out more.
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The cache key omits token_refresh_margin_seconds, but the stored deadline is calculated using that value. If two configs share the same IdP/client credentials and use different margins—the new option is explicitly configurable—the stricter config can reuse a token until the looser config's deadline. That violates the requested safety margin. We should include the margin in the key or store the token's true expiry and apply each caller's margin when reading; add a regression that uses both configs without manually clearing the cache.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
@franciscojavierarceo One behavior change worth flagging: a token already inside the margin now gets cached instead of skipped. Every read still rejects it for that caller so nothing observable changes, and a config with a smaller margin can legitimately use it. The regression exercises both configs against the shared entry without clearing the cache, and also checks the reverse direction stays cheap, with the narrow-margin config reusing what the wide one wrote. I verified it fails on the old code, with the wide config receiving the narrow config's token. I also dropped the manual cache clear from 326 permissions tests pass. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@franciscojavierarceo @ntkathole Green and ready for another look. It also still needs a |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@franciscojavierarceo @ntkathole All checks green, no conflicts with current master. It also still needs a |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@larrysingleton007 please resolve the conflicts |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@ntkathole done, master merged and pushed. The only conflict was 326 permissions tests pass on the merged tree, with ruff and mypy clean. The cache-key fix and its regression are unchanged. The other two, #6689 and #6690, merge cleanly against current master, so nothing needed there. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
@ntkathole both addressed in 2a73cb7. Pruning. Inserting on a miss now first drops entries whose stored expiry has passed. Keys are credential identities, so the cache was already bounded by the number of distinct configs, but a long-lived process that rotates credentials would have retained every retired one. Revocation. Added One deliberate deviation from what you asked, and I'd rather flag it than quietly do less. I did not retry the rejected call. All four interceptor methods share Two new tests: one asserting invalidation forces a refetch and is idempotent, one asserting an expired entry is pruned while an unexpired one survives. 328 permissions tests pass, ruff and mypy clean. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@larrysingleton007 not able to rebase and merge, can you rebase and possibly squash the commit? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
2a73cb7 to
202e30d
Compare
|
@ntkathole rebased onto current master and squashed to a single commit, 202e30d. The branch is no longer behind, and the three merge commits are gone. One note on how I did it, since it affects what you're reviewing. Master had moved three commits past this branch's last merge, so I brought those in first and then collapsed everything onto Re-verified after the rebase, since master's newer commits touched |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@ntkathole both review threads are resolved now. I'd replied in the conversation rather than inside the threads, which is why they stayed open — my mistake. Summary of where they landed: pruning on cache miss is implemented and covered by a test; eviction on Two things still block the merge, neither of them code in this PR. The failing check is @franciscojavierarceo the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Following up on the red check with evidence rather than just asking for a re-run.
Both are on master with no PR involved. The failure here, For this PR specifically, that test builds its session from So a re-run should clear it. Separately, |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
202e30d to
2a40a4f
Compare
|
One more CI follow-up: the DuckDB failure in the latest run is unrelated to this OIDC diff. I found the same extension-install lock race in 2 other PR runs, filed #6743, and put the fix in #6744. #6744 installs DuckDB's No change is needed in this PR for that failure. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
On the client_secret branch every outbound RPC built a fresh auth-token factory, manager and OIDCDiscoveryService, paying a discovery GET plus a token POST per call and discarding the token. All three auth interceptors invoke it per RPC, so a batch materialization loop multiplied IdP load by request count and could trip IdP rate limits. Measured before: 5 calls = 5 discovery GETs + 5 token POSTs. After: 1 and 1. Caches IdP tokens in a module-level dict keyed by the token-request identity, since the interceptors build a fresh manager per call and instance state would not survive. Expiry comes from the token's own exp claim, falling back to the token endpoint's expires_in; a token whose expiry is unknowable is not cached, preserving per-call behaviour for opaque tokens. The cache stores true expiry and applies the caller's token_refresh_margin_seconds on read, rather than storing a deadline. The margin is not part of the key, so baking it in let a config with a wider margin reuse a token past its own safety window when another config sharing the same credentials had written the entry. token_refresh_margin_seconds is configurable on OidcClientAuthConfig (default 30, gt=0) rather than hardcoded. Inserting on a miss prunes entries whose stored expiry has passed. Keys are credential identities so the cache is bounded by distinct configs, but a long-lived process rotating credentials would otherwise retain every retired identity. Reuse means a token the IdP revokes mid-life keeps being presented until its own expiry, where fetching per call self-corrected. Adds OidcAuthClientManager.invalidate_token and a transport-agnostic invalidate_auth_token(auth_config), wired into the gRPC interceptor: an UNAUTHENTICATED response drops the cached token so the next call refetches, bounding staleness to the rejected request. The call is not retried, because all four interceptor methods share that path and a stream's request_iterator may already be consumed. 329 permissions tests pass. Signed-off-by: Larry Singleton <[email protected]>
2a40a4f to
50c228d
Compare
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
Client-side OIDC auth pays a discovery fetch and a token fetch per outbound request
There was an error while loading. Please reload this page.