Skip to content

Commit f9e98d1

Browse files
authored
Allow providing the uv auth login password or token via stdin (#15642)
1 parent 63b93a1 commit f9e98d1

File tree

5 files changed

+105
-3
lines changed

5 files changed

+105
-3
lines changed

crates/uv-cli/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5567,12 +5567,16 @@ pub struct AuthLoginArgs {
55675567
pub username: Option<String>,
55685568

55695569
/// The password to use for the service.
5570+
///
5571+
/// Use `-` to read the password from stdin.
55705572
#[arg(long, conflicts_with = "token")]
55715573
pub password: Option<String>,
55725574

55735575
/// The token to use for the service.
55745576
///
55755577
/// The username will be set to `__token__`.
5578+
///
5579+
/// Use `-` to read the token from stdin.
55765580
#[arg(long, short, conflicts_with = "username", conflicts_with = "password")]
55775581
pub token: Option<String>,
55785582

crates/uv/src/commands/auth/login.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ pub(crate) async fn login(
8787
(None, Some(_), Some(_)) => {
8888
bail!("Cannot include a password in the URL when using `--token`")
8989
}
90+
(None, None, Some(value)) | (Some(value), None, None) if value == "-" => {
91+
let mut input = String::new();
92+
std::io::stdin().read_line(&mut input)?;
93+
input.trim().to_string()
94+
}
9095
(Some(cli), None, None) => cli,
9196
(None, Some(url), None) => url.to_string(),
9297
(None, None, Some(token)) => token,

crates/uv/tests/it/auth.rs

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
#[cfg(feature = "native-auth")]
21
use anyhow::Result;
32
use assert_cmd::assert::OutputAssertExt;
4-
#[cfg(feature = "native-auth")]
53
use assert_fs::{fixture::PathChild, prelude::FileWriteStr};
64
#[cfg(feature = "native-auth")]
75
use uv_static::EnvVars;
@@ -778,6 +776,88 @@ fn login_text_store() {
778776
);
779777
}
780778

779+
#[test]
780+
#[allow(clippy::disallowed_types)]
781+
fn login_password_stdin() -> Result<()> {
782+
let context = TestContext::new_with_versions(&[]);
783+
784+
// Create a temporary file with the password
785+
let password_file = context.temp_dir.child("password.txt");
786+
password_file.write_str("secret-password")?;
787+
788+
// Login with password from stdin
789+
uv_snapshot!(context.auth_login()
790+
.arg("https://example.com/simple")
791+
.arg("--username")
792+
.arg("testuser")
793+
.arg("--password")
794+
.arg("-")
795+
.stdin(std::fs::File::open(password_file)?), @r"
796+
success: true
797+
exit_code: 0
798+
----- stdout -----
799+
800+
----- stderr -----
801+
Stored credentials for testuser@https://example.com/
802+
"
803+
);
804+
805+
// Verify the credentials work by retrieving the token
806+
uv_snapshot!(context.auth_token()
807+
.arg("https://example.com/simple")
808+
.arg("--username")
809+
.arg("testuser"), @r"
810+
success: true
811+
exit_code: 0
812+
----- stdout -----
813+
secret-password
814+
815+
----- stderr -----
816+
"
817+
);
818+
819+
Ok(())
820+
}
821+
822+
#[test]
823+
#[allow(clippy::disallowed_types)]
824+
fn login_token_stdin() -> Result<()> {
825+
let context = TestContext::new_with_versions(&[]);
826+
827+
// Create a temporary file with the token
828+
let token_file = context.temp_dir.child("token.txt");
829+
token_file.write_str("secret-token")?;
830+
831+
// Login with token from stdin
832+
uv_snapshot!(context.auth_login()
833+
.arg("https://example.com/simple")
834+
.arg("--token")
835+
.arg("-")
836+
.stdin(std::fs::File::open(token_file)?), @r"
837+
success: true
838+
exit_code: 0
839+
----- stdout -----
840+
841+
----- stderr -----
842+
Stored credentials for https://example.com/
843+
"
844+
);
845+
846+
// Verify the credentials work by retrieving the token
847+
uv_snapshot!(context.auth_token()
848+
.arg("https://example.com/simple"), @r"
849+
success: true
850+
exit_code: 0
851+
----- stdout -----
852+
secret-token
853+
854+
----- stderr -----
855+
"
856+
);
857+
858+
Ok(())
859+
}
860+
781861
#[test]
782862
fn token_text_store() {
783863
let context = TestContext::new_with_versions(&[]);

docs/concepts/authentication/cli.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,17 @@ This will prompt for the credentials.
1515
The credentials can also be provided using the `--username` and `--password` options, or the
1616
`--token` option for services which use a `__token__` or arbitrary username.
1717

18+
!!! note
19+
20+
We recommend providing the secret via stdin. Use `-` to indicate the value should be read from
21+
stdin, e.g., for `--password`:
22+
23+
```console
24+
$ echo 'my-password' | uv auth login example.com --password -
25+
```
26+
27+
The same pattern can be used with `--token`.
28+
1829
Once credentials are added, uv will use them for packaging operations that require fetching content
1930
from the given service. At this time, only HTTPS Basic authentication is supported. The credentials
2031
will not yet be used for Git requests.

docs/reference/cli.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ uv auth login [OPTIONS] <SERVICE>
110110
<p>May also be set with the <code>UV_NO_PROGRESS</code> environment variable.</p></dd><dt id="uv-auth-login--no-python-downloads"><a href="#uv-auth-login--no-python-downloads"><code>--no-python-downloads</code></a></dt><dd><p>Disable automatic downloads of Python.</p>
111111
</dd><dt id="uv-auth-login--offline"><a href="#uv-auth-login--offline"><code>--offline</code></a></dt><dd><p>Disable network access.</p>
112112
<p>When disabled, uv will only use locally cached data and locally available files.</p>
113-
<p>May also be set with the <code>UV_OFFLINE</code> environment variable.</p></dd><dt id="uv-auth-login--password"><a href="#uv-auth-login--password"><code>--password</code></a> <i>password</i></dt><dd><p>The password to use for the service</p>
113+
<p>May also be set with the <code>UV_OFFLINE</code> environment variable.</p></dd><dt id="uv-auth-login--password"><a href="#uv-auth-login--password"><code>--password</code></a> <i>password</i></dt><dd><p>The password to use for the service.</p>
114+
<p>Use <code>-</code> to read the password from stdin.</p>
114115
</dd><dt id="uv-auth-login--project"><a href="#uv-auth-login--project"><code>--project</code></a> <i>project</i></dt><dd><p>Run the command within the given project directory.</p>
115116
<p>All <code>pyproject.toml</code>, <code>uv.toml</code>, and <code>.python-version</code> files will be discovered by walking up the directory tree from the project root, as will the project's virtual environment (<code>.venv</code>).</p>
116117
<p>Other command-line arguments (such as relative paths) will be resolved relative to the current working directory.</p>
@@ -120,6 +121,7 @@ uv auth login [OPTIONS] <SERVICE>
120121
<p>Repeating this option, e.g., <code>-qq</code>, will enable a silent mode in which uv will write no output to stdout.</p>
121122
</dd><dt id="uv-auth-login--token"><a href="#uv-auth-login--token"><code>--token</code></a>, <code>-t</code> <i>token</i></dt><dd><p>The token to use for the service.</p>
122123
<p>The username will be set to <code>__token__</code>.</p>
124+
<p>Use <code>-</code> to read the token from stdin.</p>
123125
</dd><dt id="uv-auth-login--username"><a href="#uv-auth-login--username"><code>--username</code></a>, <code>-u</code> <i>username</i></dt><dd><p>The username to use for the service</p>
124126
</dd><dt id="uv-auth-login--verbose"><a href="#uv-auth-login--verbose"><code>--verbose</code></a>, <code>-v</code></dt><dd><p>Use verbose output.</p>
125127
<p>You can configure fine-grained logging using the <code>RUST_LOG</code> environment variable. (<a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives">https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives</a>)</p>

0 commit comments

Comments
 (0)