feat(otdfctl): Store refresh tokens to extend sessions in otdfctl - #3829
feat(otdfctl): Store refresh tokens to extend sessions in otdfctl#3829elizabethhealy wants to merge 3 commits into
Conversation
|
Warning Review limit reached
Next review available in: 29 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThe PR adds a concurrent-safe, profile-backed OAuth2 token source. It refreshes expired access tokens, persists credentials, clears invalid sessions, distinguishes transient failures, and integrates these outcomes into SDK authentication and CLI warnings. ChangesAuthentication refresh flow
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant SDKAuth
participant profileTokenSource
participant ProfileStorage
participant OAuth2TokenEndpoint
SDKAuth->>profileTokenSource: request access token
profileTokenSource->>ProfileStorage: load profile credentials
profileTokenSource->>OAuth2TokenEndpoint: refresh expired access token
OAuth2TokenEndpoint-->>profileTokenSource: return refreshed credentials
profileTokenSource->>ProfileStorage: persist credentials
profileTokenSource-->>SDKAuth: return access token
Possibly related PRs
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Benchmark results, click to expandBenchmark authorization.GetDecisions Results:
Benchmark authorization.v2.GetMultiResourceDecision Results:
Benchmark Statistics
Bulk Benchmark Results
TDF3 Benchmark Results:
|
Benchmark results, click to expandBenchmark authorization.GetDecisions Results:
Benchmark authorization.v2.GetMultiResourceDecision Results:
Benchmark Statistics
Bulk Benchmark Results
TDF3 Benchmark Results:
|
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@otdfctl/pkg/auth/auth.go`:
- Around line 198-203: Update RefreshAccessToken and its eager-refresh callers
to preserve the profile’s existing refresh token when the IdP response omits a
replacement, rather than storing an empty value; retain a newly returned refresh
token when present. Add coverage in refresh_test.go for both eager-refresh paths
and the non-rotated-token response.
- Around line 217-223: Update the token-return flow around buildToken and
HasRefreshToken so an invalid access token with no refresh token is rejected by
returning ErrAccessTokenExpired instead of returning the expired token. Preserve
the existing RefreshAccessToken path and its error propagation when a refresh
token is available.
In `@otdfctl/pkg/auth/refresh.go`:
- Around line 85-90: In otdfctl/pkg/auth/refresh.go at lines 85-90, capture the
error returned by SetAuthCredentials in the isInvalidGrant path and propagate it
along with ErrRefreshTokenInvalid instead of discarding it with underscore. In
otdfctl/pkg/auth/token_source.go at lines 53-56, apply the same error-handling
approach: capture and propagate the SetAuthCredentials error before resetting
p.inner, combining it with any existing error that describes the invalid-grant
condition. Ensure both locations preserve the original invalid-token error
context while surfacing credential-clear failures.
In `@otdfctl/pkg/auth/token_source.go`:
- Around line 69-76: Update profileTokenSource.Invalidate so it both clears the
cached inner source and ensures the next Token() call cannot reuse the currently
stored unexpired access token. Record a forced-refresh state or mark the
in-memory token expired, and have the existing token-source rebuild honor that
state while preserving normal caching behavior afterward.
- Around line 62-64: Update the token refresh logic around persist so it returns
whether credential persistence succeeded, and assign cachedAccess only when that
result is successful. Preserve the token-change check so a failed write for the
same rotated token remains eligible for retry on later calls, including the
analogous flow near the second persist call.
- Around line 103-109: Update the HTTP client created in rebuild’s tlsNoVerify
branch via utils.NewHTTPClient to configure a finite http.Client.Timeout before
storing it in oauth2.HTTPClient. Preserve the existing context and TokenSource
flow while ensuring p.inner.Token cannot wait indefinitely.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 1386105b-1d70-4245-9868-40eb847c63cc
📒 Files selected for processing (8)
otdfctl/cmd/auth/login.gootdfctl/cmd/common/common.gootdfctl/pkg/auth/auth.gootdfctl/pkg/auth/errors.gootdfctl/pkg/auth/refresh.gootdfctl/pkg/auth/refresh_test.gootdfctl/pkg/auth/token_source.gootdfctl/pkg/auth/token_source_test.go
Benchmark results, click to expandBenchmark authorization.GetDecisions Results:
Benchmark authorization.v2.GetMultiResourceDecision Results:
Benchmark Statistics
Bulk Benchmark Results
TDF3 Benchmark Results:
|
|
Summary
oauth2.TokenSourcewith a newprofileTokenSourcethat refreshes via the IdP and persists rotated credentials back to the profile storeValidateProfileAuthCredentials) and inGetTokenWithProfile(used byprint-access-token) so errors surface up-frontinvalid_grant(expired/revoked refresh token) to cleanly prompt re-login instead of surfacing a raw OAuth errorslog.Infoto stderr when a refresh occurs, andslog.Warnon failuresBackground
The PKCE login flow already captured and stored the refresh token, but it was never used — the CLI exited with "Access token expired. Please login" on every expiry. App sec has confirmed no FedRAMP/compliance concerns with storing refresh tokens in the profile, with a recommendation to prefer OS keychain storage.
Follow-ups
go-osprofiles)OTDFCTL_TOKEN_EXPIRY_BUFFERenv varChecklist
Testing Instructions
Summary by CodeRabbit
New Features
Bug Fixes