-
Notifications
You must be signed in to change notification settings - Fork 170
feat(auth): persist OAuth tokens across workload restarts #3382
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.
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
feat(auth): persist OAuth tokens across workload restarts #3382
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3382 +/- ##
==========================================
- Coverage 64.83% 64.71% -0.13%
==========================================
Files 380 381 +1
Lines 37004 37121 +117
==========================================
+ Hits 23992 24022 +30
- Misses 11127 11217 +90
+ Partials 1885 1882 -3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
15cb24f to
157452f
Compare
157452f to
76eb7a2
Compare
This change enables remote MCP servers (like Datadog and Glean) to restore their OAuth sessions after workload restarts without requiring a new browser-based login. Changes: - Add CachedAccessToken, CachedRefreshToken, CachedTokenExpiry fields to remote.Config for token persistence - Create PersistingTokenSource wrapper to save tokens when refreshed - Modify Handler.Authenticate to restore from cached tokens when available - Add token persister callback in runner to save tokens to config state Fixes stacklok#3331 Signed-off-by: Frédéric LE FEURMOU <flfeurmou@indeed.com>
76eb7a2 to
4fff5af
Compare
Signed-off-by: Frédéric LE FEURMOU <flfeurmou@indeed.com>
amirejaz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your contribution @flfeurmou-indeed 🚀 . The PR looks good overall. I’ve added a few comments.
Address reviewer feedback: - Store refresh tokens securely in secret manager instead of plain text config - Remove access token caching (can be regenerated from refresh token) - Add TokenTypeOAuthRefreshToken for proper secret categorization - Gracefully handle missing secret manager (tokens won't persist but OAuth works) Security improvement: tokens are now stored in OS keyring/encrypted storage, config only contains the secret reference (e.g., OAUTH_REFRESH_TOKEN_workload) Signed-off-by: Frédéric LE FEURMOU <flfeurmou@indeed.com>
Address reviewer nit stacklok#3: DetectAuthenticationFromServer and discoverIssuerAndScopes were called in both tryRestoreFromCachedTokens and authenticateWithOAuth. Now these are called once in Authenticate and the results are passed to both tryRestoreFromCachedTokens and performOAuthFlow, avoiding redundant network calls. Signed-off-by: Frédéric LE FEURMOU <flfeurmou@indeed.com>
|
@flfeurmou Thanks for addressing the feedback. This looks solid now, I’ve added one additional comment. Sorry for the fragmented review. |
Avoids unnecessary writes since refresh tokens are long-lived and usually don't change on every access-token refresh.
|
@flfeurmou-indeed During further testing, I realized we need to either persist the client credentials (i.e., We already have an issue open to track persisting client credentials: #3335. |
Some MCP servers require client credentials for token refresh. When no configured client_id exists and a registration endpoint is available, perform dynamic client registration before attempting to restore from cached tokens. This defers stacklok#3335 (persisting client credentials) to a separate PR while still allowing token restoration to work for servers that require client credentials.
|
Added getClientCredentials() that does dynamic registration when no client_id is configured. So when restoring from cached tokens, we re-register the client first (if DCR is available), then use those credentials for the token refresh. This keeps this PR simple and defers the full client credentials persistence (#3335) to a separate PR. |
|
@flfeurmou-indeed I tested the flow with the latest code and noticed that some servers reject newly generated client credentials because they differ from the original ones. I got the following error: I think we can merge this PR based on the second-to-last commit, without re-registering the client, and prioritize #3335 separately. Please let me know if that works for you, or if you have another suggestion. |
…ens" This reverts commit c82408a.
Works for me. Reverted the DCR commit, so this PR is back to just the token persistence with the refresh-token-change optimization. Happy to merge as-is and tackle #3335 as a quick follow-up since this PR lays most of the groundwork. Let me know if there's anything else needed. |
|
@flfeurmou-indeed Approved 👍 we can merge once CI is green. |
|
@flfeurmou-indeed I was going to assign #3335 to you, but it’s already assigned to someone else. |
|
@flfeurmou-indeed awesome, please file the PR. I will review it tomorrow. |
Summary
This change enables remote MCP servers (like Datadog and Glean) to restore their OAuth sessions after workload restarts without requiring a new browser-based login.
Problem
When using remote MCP servers with OAuth authentication (e.g., Datadog, Glean), users must re-authenticate via browser every time:
This creates significant friction, especially for developers who frequently close their laptops (e.g., commuting).
Solution
Persist OAuth tokens (access token, refresh token, expiry) to the run configuration, enabling automatic session restoration:
Token caching in config: Added
CachedAccessToken,CachedRefreshToken,CachedTokenExpiryfields toremote.ConfigPersistingTokenSource wrapper: Intercepts token refreshes and persists updated tokens to disk
Session restoration: On workload start, checks for cached tokens and restores the session without browser interaction
Automatic refresh persistence: When tokens are auto-refreshed by the oauth2 library, the new tokens are saved
Changes
pkg/auth/remote/config.go: Added token caching fields and helper methodspkg/auth/remote/handler.go: Restore from cached tokens, persist after OAuth flowpkg/auth/remote/persisting_token_source.go: New wrapper to persist refreshed tokenspkg/auth/discovery/discovery.go: Expose token details inOAuthFlowResultpkg/runner/runner.go: Set up token persister callbackTesting
PersistingTokenSourceConfig.HasValidCachedTokens()andConfig.ClearCachedTokens()Fixes
Fixes #3331