Secure authentication method needed
I would like to use uv to install dependencies from a private artifactory repository. Authentication for the private repository works with username and password. I would also like to store the index-url in the pyproject.toml. This simplifies the usage of uv for every team-member. The current minimal config looks like the following:
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = []
[tool.uv]
index-url = "https://artifactory.company.com/pypi/simple"
native-tls = true
keyring-provider = "subprocess"
The documentation for authentication lists these options:
- The URL, e.g.,
https://<user>:<password>@<hostname>/... - A .netrc configuration file
- A keyring provider (requires opt-in)
The first option is not possible, as the credentials would be shared via git. Using .netrc is only partially possible as it poses a security risk by storing credentials in plain text. I tried this option nonetheless and it worked. But I would like to avoid it due to the plain-text password.
The thrid option does not work. I will describe in detail, what I tried and where it failed.
pip install keyringkeyring set artifactory.company.com my_usernameand entering the credentials as prompted- running
uv add <any-package>with the configuration from above failed with
hint: An index URL (https://artifactory.company.com/pypi/simple) could not be queried due to a lack
of valid authentication credentials (401 Unauthorized).
I changed the configuration value for index-url to https://[email protected]/pypi/simple. By this uv reads the credentials from keyring. As this stores the username in the pyproject.toml the approach does not work. Every team-member uses their own credentials to authenticate with the private artifactory.
- uv version:
uv 0.4.25 (97eb6ab4a 2024-10-21) - uv platform:
Windows 11
If uv should work with the plain index-url (no username) and keyring, I would like to report this as a bug. In case it doesn't, I would like to request a secure methode for storing credentials while preserving a transferable uv configuration.
Keyring alway require a username. If you name your index, you can provide credentials for your index via environment variables and store them however you want:
[[tool.uv.index]]
name = "company"
url = "https://artifactory.company.com/pypi/simple"
default = true
Then you can specify a username with UV_INDEX_COMPANY_USERNAME and a password with UV_INDEX_COMPANY_PASSWORD. See: https://docs.astral.sh/uv/configuration/indexes/#providing-credentials.
@charliermarsh, thanks for pointing out the solution using environment variables.
Do you currently plan to provide a solution similar to poetry config http-basic? This uses keyring under the hood, but stores which username to use for each index on the user-level.
Using your example, I would propose the following workflow:
- Define the index (making this possible via cli, e.g.
uv index add ...would be nice to have)
[[tool.uv.index]]
name = "company"
url = "https://artifactory.company.com/pypi/simple"
default = true
- Define credentials with
uv index config company "my_username". This will prompt for the password. The username is stored in$XDG_CONFIG/uv/uv.toml, e.g.
[index.company]
username = "my_username"
The password is stored with keyring.
Setting this config up once, makes it possible to run uv add <package>. Which is then pointing to the right index and using credentials automatically
Thought this might help. I've made a hacky way to work as a "config" to set the environment variables to authenticate for private pypi repo. My company uses aws so the tokens are short lived, so it's safe to do.
For more info, you would replace PASSWORD with the CodeArtifact auth command to retrieve the password token automatically and set the new credentials when it expires.
#set_credentials.sh
if [ ! -f ~/.uv ]; then
touch ~/.uv
fi
update_credentials() {
local PASSWORD="updated_password"
# Set UV_INDEX_INTERNAL_COMPANY_PASSWORD in the ~/.uv file
if grep -q "^export UV_INDEX_INTERNAL_COMPANY_PASSWORD=" ~/.uv; then
# Use sed to replace the value with the local $PASSWORD variable
sed -i '' "s/^export UV_INDEX_INTERNAL_COMPANY_PASSWORD=.*/export UV_INDEX_INTERNAL_COMPANY_PASSWORD=${PASSWORD}/" ~/.uv
else
# Append the line with $PASSWORD if it doesn't already exist
echo "export UV_INDEX_INTERNAL_COMPANY_USERNAME=aws" >> ~/.uv
echo "export UV_INDEX_INTERNAL_COMPANY_PASSWORD=${PASSWORD}" >> ~/.uv
fi
}
update_credentials
source ~/.uv
Run this using
source set_credentials.sh
Do you currently plan to provide a solution similar to poetry config http-basic? This uses keyring under the hood, but stores which username to use for each index on the user-level.
Yeah I'm interested in this, but we'll need to switch from the Python keyring library to something more robust and Rust native which will take time.
Hello,
I run into the same kind of question: What is the best way of authenticate on private indexes with uv?
If I understand well, the recommended solution today is to use the UV_INDEX_XXX env variables.
However, this is not a very practical solution as .env file is not supported by uv sync, which means we have to set numerous specific variables for uv.
I ended up using the same "hacky" (or at least non-standard) solution as described here, with a set_env.sh script in my repo setting the UV_INDEX_XXX env variables to the appropriate values on both devs and CI machines.
In my case, I see two solutions which would be less "hacky" and much more practical:
- accepting
.envfile in theuv synccommand, - or interpreting env variables in pyproject.toml.
Do you have any of these two planned or do you have something else to recommend?
Thank you 🙏
accepting .env file in the uv sync command,
Why not activate your .env file with another tool like direnv? Why is it uv's job to read configuration from that file?
interpreting env variables in pyproject.toml
Wouldn't you still need to set the variables? Isn't that part of the complaint?
I believe you can also configure your credentials in the uv.toml, if you prefer.
interpreting env variables in pyproject.toml
Wouldn't you still need to set the variables? Isn't that part of the complaint?
I believe you can also configure your credentials in the
uv.toml, if you prefer.
The thing that makes it a bit more complicated with uv for me (and my team) is that uv has a strict convention about the env variable names for authentification. With other tools allowing using env variables directly in config files, we can easily have a universal configuration across tools that work on both devs and CI machines with simple setup, which is quiet convenient.
accepting .env file in the uv sync command,
Why not activate your
.envfile with another tool likedirenv? Why is it uv's job to read configuration from that file?
I must admit I am not familiar with direnv as I never needed it. It may be a very good solution indeed, I'll have a deeper look at it.
Thank you for the answer 🙏
With other tools allowing using env variables directly in config files, we can easily have a universal configuration across tools that work on both devs and CI machines with simple setup, which is quiet convenient.
Can you share some example tools? I'd like to look at their approaches.
Can you share some example tools? I'd like to look at their approaches.
First, I need to say that I don't come from the Python world, I only started using Python at production scale quite recently. My programming background is mostly C++ and iOS/Android development, some UNIX system administration, Docker and k8s deployment, and Matlab quite some time ago.
I used quite a lot of different tools for configuration, environment management, compilation, testing, packaging or deployment, and most of them support reading environment variables within their "configuration files" (or equivalent). A few examples:
bundlerin gemfiles,fastlaneand other Ruby-based tools,cmakeandgradle,- I'm pretty sure Conan and Bazel support env variables one way or another but I'm not very familiar with these,
- Docker in Dockerfiles and at runtime,
- make in makefiles,
- in Python, I have used
setuptoolswithsetup.py, which is native python giving you the ability to useos.environ, - gitlab CI yaml
- helm charts for k8s
Many of these tools support env variables the same way you use it in shell: $MY_ENV_VAR.
The ENV['MY_ENV_VAR'] syntax is also present in several of these tools and I'm pretty sure I've seen it in other contexts. cmake has it a bit different with $ENV{MY_ENV_VAR}.
Hope this helps :)
Do you currently plan to provide a solution similar to poetry config http-basic? This uses keyring under the hood, but stores which username to use for each index on the user-level.
Yeah I'm interested in this, but we'll need to switch from the Python keyring library to something more robust and Rust native which will take time.
Would keyring-rs be an option: https://docs.rs/keyring/latest/keyring/ to speed up transition?
Yeah we'd probably use that, though I haven't audited it.
Thanks @ThomasHezard !
I would suggest the following implementation for Keyring:
- define index in pyproject.toml
[[tool.uv.index]]
name = “service-name”
url = “https://artifactory.company.com/*/simple”
-
keyring set artifactory.company.com my_username -> enter password
-
uv reads username and password using Keyring get_credentials service-name
This would allow teams to use keyring without making changes to pyproject.toml. Due to lack of encryption and simple logging it should be avoided to use environment variables as storage for credentials.
This approach could be quickly implemented temporarily with keyring as a Python package. When switching to Keyring-RS at a later date, the process would not need to change.
@zanieb, what are your concerns about keyring-rs? I would be interested to contribute the keyring credentials feature and would like to understand the constraints
@farndt, can you share some documentation about get_credentials without a username? afaik, you need a service and a username to reference keyring entries.
No concerns; we just audit new dependencies for trustworthiness, correctness, etc.
Feel free to contribute support if you're interested. I think the first step is to have --keyring-provider <something> use the crate as a backend?
I would suggest the following:
uv addloads the credentials for all indexes into the cache- This uses
[Index.credentials](https://github.com/astral-sh/uv/blob/main/crates/uv-distribution-types/src/index.rs#L141-L152)- This currently only supports env variables
UV_INDEX_XXXand[Credentials.from_url](https://github.com/astral-sh/uv/blob/main/crates/uv-auth/src/credentials.rs#L119-L122) - I would suggest to add logic for loading username from
auth.tomlin uv cache dir and checking keyring inIndex.credentials
- This currently only supports env variables
- To configure the credentials I would add a new command
index.- This provides
uv index auth <index-name>. - It uses the already existing
[keyring provider](https://github.com/astral-sh/uv/blob/main/crates/uv-auth/src/keyring.rs)
- This provides
As there is already an implementation for keyring within uv, I would rely on it for this topic. Using keyring-rs can then be dealt with independently, if desired.
@zanieb, what are your concerns about keyring-rs? I would be interested to contribute the keyring credentials feature and would like to understand the constraints
@farndt, can you share some documentation about get_credentials without a username? afaik, you need a service and a username to reference keyring entries.
@stoney95 Coming from python keyring: https://github.com/jaraco/keyring?tab=readme-ov-file#api it has get_credential(service, username) but username can be anything and you will recieve username related to service.
I am not sure but you could recieve username as attribute of the service. For the request may the username os variable can be used? https://github.com/hwchen/keyring-rs/blob/master/src/windows.rs comments at begin of file (line 24)
The [get_attributes](crate::Entry::get_attributes) call will return the values in the
`username`, `comment`, and `target_alias` fields (using those strings as the attribute names),```
Hello! I encountered a similar issue when trying to install from a wheel that is publicly available. I've tried [[tool.uv.index]] as well but had no luck. To reproduce:
uv pip install pytorch3d --extra-index-url https://dl.fbaipublicfiles.com/pytorch3d/packaging/wheels/py310_cu121_pyt241/pytorch3d-0.7.8-cp310-cp310-linux_x86_64.whl
× No solution found when resolving dependencies:
╰─▶ Because only the following versions of pytorch3d are available:
pytorch3d==0.1.1
pytorch3d==0.2.0
pytorch3d==0.2.5
pytorch3d==0.3.0
pytorch3d==0.4.0
pytorch3d==0.5.0
pytorch3d==0.6.1
pytorch3d==0.6.2
pytorch3d==0.7.0
pytorch3d==0.7.1
pytorch3d==0.7.2
pytorch3d==0.7.3
pytorch3d==0.7.4
and pytorch3d<=0.6.2 has no wheels with a matching Python ABI tag, we can conclude
that pytorch3d<=0.6.2 cannot be used.
And because pytorch3d==0.7.0 has no wheels with a matching platform tag and
pytorch3d==0.7.1 has no wheels with a matching Python ABI tag, we can conclude that
pytorch3d<0.7.2 cannot be used.
And because pytorch3d>=0.7.2 has no wheels with a matching platform tag and you
require pytorch3d, we can conclude that your requirements are unsatisfiable.
hint: An index URL
(https://dl.fbaipublicfiles.com/pytorch3d/packaging/wheels/py310_cu121_pyt241/pytorch3d-0.7.8-cp310-cp310-linux_x86_64.whl)
could not be queried due to a lack of valid authentication credentials (403
Forbidden).
However on the same machine, we can download the wheel with wget:
wget https://dl.fbaipublicfiles.com/pytorch3d/packaging/wheels/py310_cu121_pyt241/pytorch3d-0.7.8-cp310-cp310-linux_x86_64.whl
--2025-02-05 09:15:31-- https://dl.fbaipublicfiles.com/pytorch3d/packaging/wheels/py310_cu121_pyt241/pytorch3d-0.7.8-cp310-cp310-linux_x86_64.whl
Resolving dl.fbaipublicfiles.com (dl.fbaipublicfiles.com)... 108.156.184.100, 108.156.184.129, 108.156.184.78, ...
Connecting to dl.fbaipublicfiles.com (dl.fbaipublicfiles.com)|108.156.184.100|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 20521514 (20M) [binary/octet-stream]
Saving to: ‘pytorch3d-0.7.8-cp310-cp310-linux_x86_64.whl’
pytorch3d-0.7.8-cp310-c 100%[=============================>] 19.57M 16.9MB/s in 1.2s
2025-02-05 09:15:33 (16.9 MB/s) - ‘pytorch3d-0.7.8-cp310-cp310-linux_x86_64.whl’ saved [20521514/20521514]
Could we get some help? Thanks in advance!
I can confirm that I can download the file. I can't really speculate as to why they'd throw a 403 there. This looks unrelated to the topic in this issue though, please see #9452
I'm facing a similar issue as @stoney95 where we're using artifactory with every team member & CI pipeline having unique username & password credentials. An additional wrinkle is that each team has a separate artifactory repo, so managing dependencies from different teams is already a bit of a pain (independent of uv).
I'm trying to encourage folks to move to uv from poetry and this seems to be the first major hiccup I've run into where things are decidedly worse than poetry. At least without something like config.http-basic. All options I can think of require an additional step or tool on top of learning uv which is a pain.
Our options:
- Use keychain
- Requires specifying username in index url. Works okay for users who want to set up indexes in a global uv.toml config and use
uv pipin place ofpip, but is a non-starter for indexes in pyproject.toml - Our repos are on the same host (or VIP or whatever) & keychain auth is at the host level so multilple repos aren't too bad
- requires keychain
- Requires specifying username in index url. Works okay for users who want to set up indexes in a global uv.toml config and use
- Use
UV_INDEX_{name}_USERNAMEand_PASSWORD- Works for projects, but needs additional config. We can do something like:
but we need to manually source that file each time we work with the project, or adopt a tool likeexport UV_INDEX_A_USERNAME=${ARTIFACTORY_USER} export UV_INDEX_A_PASSWORD=${ARTIFACToRY_PASS} export UV_INDEX_B_USERNAME=${ARTIFACTORY_USER} export UV_INDEX_B_PASSWORD=${ARTIFACTORY_PASS}direnv(which I'd never heard of before this thread - Requires making changes in two places whenever adding an index (
pyproject.tomland wherever env vars are configured)
- Works for projects, but needs additional config. We can do something like:
I'm trying to use keychain on my Mac.
I did:
UV_PREVIEW_FEATURES=native-auth uv auth login https://www.reportlab.com/pypi/
entered login and password and I can see in keychain an entry:uv:https://www.reportlab.com/pypi/
Then I tried:
UV_PREVIEW_FEATURES=native-auth uv sync
but it failed with:
$ UV_PREVIEW_FEATURES=native-auth uv sync on git:main|✚1-1…9
Resolved 86 packages in 2ms
× Failed to download `preppy==5.0.1`
├─▶ Failed to fetch: `https://www.reportlab.com/pypi/packages/preppy-5.0.1-py3-none-any.whl`
╰─▶ HTTP status client error (401 Unauthorized) for url (https://www.reportlab.com/pypi/packages/preppy-5.0.1-py3-none-any.whl/)
help: `preppy` (v5.0.1) was included because `pdfgen-showdown` (v0.1.0) depends on `rlextra` (v4.4.3.1) which depends on `preppy`
If I register in plain (~/.local/share/uv/credentials/credentials.toml), it works.
If I use:
export UV_INDEX_REPORTLAB_USERNAME="..."
export UV_INDEX_REPORTLAB_PASSWORD="..."
it works as well.
I'm simply failing to get it to work with UV_PREVIEW_FEATURES=native-auth.
The native store doesn't support "prefix" matches yet. I think we need to fix that before you example will work. Can you login to www.reportlab.com instead? (We do support domain-level matches)
p.s. Thank you for trying the feature and taking the time to provide feedback!
Yes, I can login to www.reportlab.com using the same credentials I use above.
I'm not sure if I understand:
The native store doesn't support "prefix" matches yet or We do support domain-level matches
But let me know when you guys think Mac keychain should be working.
The native store doesn't support "prefix" matches yet. I think we need to fix that before you example will work. Can you login to
www.reportlab.cominstead? (We do support domain-level matches)p.s. Thank you for trying the feature and taking the time to provide feedback!
I can confirm the issue and tried the domain login you suggested, means using
UV_PREVIEW_FEATURES=native-auth uv auth login www.my_company.com
Then I try:
UV_PREVIEW_FEATURES=native-auth uv -v sync
and see in the debug output, that uv is looking for plain text credentials:
DEBUG Loaded credential file /home/...
...
DEBUG Checking text store for credentials for https://www.my_company.com/...
...
╰─▶ Missing credentials for https://www.my_company.com/...
If I login with plain text storage, UV_PREVIEW_FEATURES=native-auth uv -v sync also works.
Thanks @RStreitfeld I'll look into that!