feat: exchange OIDC identity tokens for an API key in rsconnect login#802
Merged
Conversation
|
☂️ Python Coverage
Overall Coverage
New FilesNo new covered files... Modified Files
|
atheriel
reviewed
Jun 26, 2026
atheriel
left a comment
Contributor
There was a problem hiding this comment.
This makes complete sense to do.
nealrichardson
added a commit
that referenced
this pull request
Jun 26, 2026
- Rename --token to --identity-token to disambiguate from other token types; add --identity-token-file plus CONNECT_IDENTITY_TOKEN and CONNECT_IDENTITY_TOKEN_FILE env vars so the plaintext token need not appear in process args or CI/CD logs. - Preflight the exchange via OAuth discovery: confirm the server supports the token-exchange grant (grant_types_supported) and use the discovered token endpoint (which also carries any path prefix), instead of relying on a 404 from a blind POST. - Generalize wording away from "trusted publishing" since this flag can also serve identity federation. - Fold the rsconnect login features into a single CHANGELOG bullet. Addresses review feedback from @atheriel on #802. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap Connect's trusted-publishing auth flow as a CLI command. Passing `rsconnect login <server> --token <oidc-token>` exchanges an OIDC token (e.g. a GitHub Actions OIDC token) for a short-lived Connect API key via an RFC 8693 token exchange against `/oauth/v1/token`, then validates and saves the key as the server credential. Use `--token -` to read the token from stdin. Closes #800 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
exchange_token_for_api_key() stripped the server URL to scheme://netloc before POSTing to /oauth/v1/token, which broke Connect servers configured behind a path prefix (e.g. https://host/connect). Pass the full URL to HTTPServer so the token-exchange path is appended relative to any prefix. Found by roborev review (job 58). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
exchange_token_for_api_key() read response.status without first checking response.exception. On a network/TLS failure HTTPServer.request() returns an HTTPResponse with no status set, so the access raised AttributeError (an internal error) instead of an actionable RSConnectException. Check response.exception first and raise with connection context, matching the pattern in RSConnectClient. Found by roborev review (job 59). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Rename --token to --identity-token to disambiguate from other token types; add --identity-token-file plus CONNECT_IDENTITY_TOKEN and CONNECT_IDENTITY_TOKEN_FILE env vars so the plaintext token need not appear in process args or CI/CD logs. - Preflight the exchange via OAuth discovery: confirm the server supports the token-exchange grant (grant_types_supported) and use the discovered token endpoint (which also carries any path prefix), instead of relying on a 404 from a blind POST. - Generalize wording away from "trusted publishing" since this flag can also serve identity federation. - Fold the rsconnect login features into a single CHANGELOG bullet. Addresses review feedback from @atheriel on #802. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
e062194 to
079d141
Compare
exchange_token_for_api_key() posted to only the path of the discovered token_endpoint, dropping any params/query. If a server advertises a token endpoint with a query component, the exchange would hit the wrong URL. Reconstruct the full request target (path, params, query) via urlunparse. Found by roborev review (job 65). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
login --token for OIDC trusted-publishing token exchangersconnect login
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.
Summary
Closes #800.
Adds OIDC identity-token exchange to the
rsconnect logincommand, so trusted publishers (e.g. GitHub Actions) can obtain a Connect API key without interactive OAuth login.How it works
rsconnect login <server> --identity-token <oidc-token>(or--identity-token-file <path>, or theCONNECT_IDENTITY_TOKEN/CONNECT_IDENTITY_TOKEN_FILEenv vars). Use--identity-token -to read from stdin; prefer the file/env forms to keep the secret out of process arguments and CI logs.grant_type=token-exchange,subject_token_type=id_token,requested_token_type=access_token). Connect verifies the token against a configured trusted publisher and mints a short-lived API key.test_server+test_api_key) and stores it as the server credential, honoring--name,--insecure,--cacert, and--no-set-default. Subsequentdeploy/list/etc. then just work.Failure responses are mapped to actionable errors: unsupported token exchange, connection/TLS errors, and
invalid_grant(ambiguous match, verification failure, no match).Testing
tests/test_oauth.pycover metadata discovery, the exchange success/error paths, endpoint preservation, connection-error handling, and the CLI flag / file / stdin / empty-token behavior.blackandflake8clean on changed files;pyrightreports only pre-existing errors.🤖 Generated with Claude Code