Repository navigation
Add rsconnect token to print a stored OAuth access token - #884
Merged
Merged
Conversation
Other tools, such as the Connect SDKs, can run `rsconnect token` to make API requests with the user's `rsconnect login` instead of a separate API key, without reading or refreshing the credential store themselves. The command prints the stored token without contacting the server. After a 401, callers run it with --refresh. Refreshing goes through a new shared `refresh_stored_tokens`, which the client's own 401 retry also uses. It holds a cross-process lock and skips the exchange when another process has already replaced the rejected token, so concurrent refreshes don't invalidate each other's rotated refresh tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
☂️ Python Coverage
Overall Coverage
New FilesNo new covered files... Modified Files
|
tdstein
self-requested a review
October 26, 2026 14:21
tdstein
approved these changes
Oct 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Intent
Other tools need a way to use the credentials from
rsconnect login. The motivating case is the Connect SDKs (posit-sdk-py, connectapi), which still require an API key. Reading the keyring andservers.jsondirectly would be fragile. The storage format is internal, refresh tokens rotate (so a second process that refreshes would break the stored login), and R can't easily read a keyring entry written by Python. A command keeps rsconnect as the only thing that reads, refreshes, and writes stored credentials, following the same pattern asgh auth tokenandgcloud auth print-access-token.Design discussion: posit-dev/posit-cli#21. posit-cli mounts rsconnect's commands, so this also becomes
posit connect token.Type of Change
Approach
token --refreshand retries once.--format jsonprints{"access_token", "token_type": "Bearer", "server"}.0on success,4when the user needs to log in again (no stored OAuth credentials, a server not added with OAuth, or the server rejected the refresh token), and1for anything else (for example, an unreachable server).4matchesgh. Nothing is printed on stdout on failure.posit connect login.One refresh path, serialized. A new
rsconnect.oauth.refresh_stored_tokens(..., rejected_access_token=...)holds a cross-process file lock (fcntl.flockon POSIX,msvcrt.lockingon Windows) in the config directory. While holding it, the function re-reads the stored access token. If that token no longer matches the rejected one, another process has already refreshed, and the function returns that token without a second exchange.RSConnectClient._attempt_token_refresh(the 401 retry used by every command) now calls this function too. So concurrentdeploys andtoken --refreshcalls no longer invalidate each other's rotated refresh tokens. If the lock file can't be created, the refresh runs unlocked, as it did before.Behavior changes to the existing 401 retry:
Out of scope: Connect Cloud logins. They have their own token model, and
tokenreports them as not added with OAuth login.Automated Tests
tests/test_oauth_token.pycovers:NotLoggedInError,InvalidGrantError(stored tokens kept), andInvalidClientError(tokens cleared, new client registered);-n/-s/default server,--refresh, exit codes 4 and 1, never printing an API key, and the login hint under a mounting CLI.A new autouse fixture in
tests/conftest.pykeeps the lock file out of the real config directory.Directions for Reviewers
Against a server you've logged in to with
rsconnect login:I ran this against a real server: the stale token got a 401, and after
--refreshthe new token got a 200 and was saved. 5 concurrenttoken --refreshprocesses all exited 0 with the same single token, and that token worked. Against a login whose refresh token had expired, the command exited with 4 and printed the login hint.Checklist
rsconnect-python-tests-at-nightworkflow in Connect against this feature branch.🤖 Generated with Claude Code