Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uv pip install resulting in 401 Unauthorized with private index url #1709

Closed
AloizioMacedo opened this issue Feb 19, 2024 · 33 comments · Fixed by #1886, prefix-dev/async_http_range_reader#9 or #1902
Assignees
Labels
registry Related to package indexes and registries

Comments

@AloizioMacedo
Copy link

I am trying to use uv pip install with a private repository, and I am getting (401 Unauthorized errors). The same URL works completely fine with native pip, i.e. if I just remove the "uv".

I cannot share the real URL, but there might be characters which might be raising some issues (like '@'), so here is a pseudo-example:

uv pip install privatepackage --index-url https://[email protected]:[email protected]/path1/path2/path3/path4/path5

My current uv version is 0.1.5, and the Python env is 3.8.

@charliermarsh
Copy link
Member

Are you able to share the hosting provider (or similar) for the private repository? E.g., is it on Azure?

@zanieb zanieb added the registry Related to package indexes and registries label Feb 19, 2024
@AloizioMacedo
Copy link
Author

Thanks for the (extremely quick!!!) response! I'm sorry, but I can't share more details about the private index.

If it might be helpful, I explicitly mentioned the @ character because some of our developers had problems in the past due to '@' being converted into '%40' at some point depending on the setup, and I see this happening during the uv_client::html::parse stage in verbose mode.

@hwong557
Copy link

hwong557 commented Feb 19, 2024

I have the same problem. I'm using a jfrog repo. The index url looks like:

https://$USERNAME:[email protected]/yyy/zzz

@charliermarsh
Copy link
Member

Are you able to share the full output of running with --verbose? You're welcome to remove the domain and obviously the credentials. We're focusing on improving support for private indexes this week.

@hwong557
Copy link

I ran

 uv pip compile pyproject.toml --extra-index-url https://$USERNAME:[email protected]/yyy/zzz -o requirements.txt

and got:

error: Failed to download: my_private_package=1.2.3
  Caused by: The wheel my_private_package1.2.3-py3-none-any.whl is not a valid zip file
  Caused by: an upstream reader returned an error: io error occurred: Request error: HTTP status client error (401 Unauthorized) for url (https://xxx.jfrog.io/yyy/my_private_package1.2.3/my_private_package1.2.3-py3-none-any.whl#sha256=1111111111111111111111111111111111111111111111111111111111111111)
  Caused by: io error occurred: Request error: HTTP status client error (401 Unauthorized) for url (https://xxx.jfrog.io/yyy/my_private_package1.2.3/my_private_package1.2.3-py3-none-any.whl#sha256=1111111111111111111111111111111111111111111111111111111111111111)
  Caused by: Request error: HTTP status client error (401 Unauthorized) for url (https://xxx.jfrog.io/yyy/my_private_package1.2.3/my_private_package1.2.3-py3-none-any.whl#sha256=1111111111111111111111111111111111111111111111111111111111111111)
  Caused by: HTTP status client error (401 Unauthorized) for url (https://xxx.jfrog.io/yyy/my_private_package1.2.3/my_private_package1.2.3-py3-none-any.whl#sha256=1111111111111111111111111111111111111111111111111111111111111111)

Running the above command with -i instead of --extra-index-url gives similar output, but it fails on a public package.

@zanieb
Copy link
Member

zanieb commented Feb 19, 2024

@mpizenberg
Copy link

mpizenberg commented Feb 20, 2024

Same issue for me with a private gitlab index

> uv pip compile --index-url='https://__token__:[email protected]/api/v4/projects/$ID/packages/pypi/simple' --output-file=requirements/linux-uv.txt pyproject.toml
error: Failed to download: my-pkg==0.4.0
  Caused by: HTTP status client error (401 Unauthorized) for url (https://gitlab.com/api/v4/projects/$ID/packages/pypi/files/.../my_pkg-0.4.0-py3-none-any.whl#sha256=...)

@nitlev
Copy link

nitlev commented Feb 20, 2024

Same error here on an GCP Arctifact Registry:

> uv pip install my-pkg --extra-index-url "https://_json_key_base64:***@europe-west1-python.pkg.dev/project/repo/simple/"
uv_client::flat_index::from_entries 
 uv_resolver::resolver::solve 
   uv_resolver::resolver::choose_version package=root
   uv_resolver::resolver::get_dependencies package=root, version=0a0.dev0
      0.046054s   0ms INFO pubgrub::internal::partial_solution add_decision: root @ 0a0.dev0
   uv_resolver::resolver::choose_version package=my-pkg
     uv_resolver::resolver::package_wait package_name=my-pkg
 uv_resolver::resolver::process_request request=Versions my-pkg
   uv_client::registry_client::simple_api package=my-pkg
     uv_client::cached_client::get_cacheable 
       uv_client::cached_client::read_and_parse_cache file=/root/.cache/uv/simple-v1/pypi/my-pkg.rkyv
 uv_resolver::resolver::process_request request=Prefetch my-pkg *
          0.046664s   0ms WARN uv_client::cached_client Broken cache entry at /root/.cache/uv/simple-v1/pypi/my-pkg.rkyv, removing: failed to open file `/root/.cache/uv/simple-v1/pypi/my-pkg.rkyv`
       uv_client::cached_client::fresh_request url="https://pypi.org/simple/my-pkg/"
     uv_client::cached_client::get_cacheable 
       uv_client::cached_client::read_and_parse_cache file=/root/.cache/uv/simple-v1/0c1fe98321190311/my-pkg.rkyv
       uv_client::cached_client::revalidation_request url="https://europe-west1-python.pkg.dev/project/repo/simple/my-pkg/"
       uv_client::cached_client::new_cache file=/root/.cache/uv/simple-v1/0c1fe98321190311/my-pkg.rkyv
       uv_client::registry_client::parse_simple_api package=my-pkg
         uv_client::html::parse url=https://_json_key_base64:***@europe-west1-python.pkg.dev/project/repo/simple/my-pkg/
 uv_resolver::version_map::from_metadata 
   uv_distribution::distribution_database::get_or_build_wheel_metadata dist=my-pkg==0.0.1
     uv_client::registry_client::wheel_metadata built_dist=my-pkg==0.0.1
       uv_client::cached_client::get_serde 
         uv_client::cached_client::get_cacheable 
           uv_client::cached_client::read_and_parse_cache file=/root/.cache/uv/wheels-v0/index/0c1fe98321190311/my-pkg/my-pkg-0.0.1-py3-none-any.msgpack
   uv_resolver::resolver::get_dependencies package=my-pkg, version=0.0.1
     uv_resolver::resolver::distributions_wait package_id=my-pkg-0.0.1
              0.187447s   0ms WARN uv_client::cached_client Broken cache entry at /root/.cache/uv/wheels-v0/index/0c1fe98321190311/my-pkg/my-pkg-0.0.1-py3-none-any.msgpack, removing: failed to open file `/root/.cache/uv/wheels-v0/index/0c1fe98321190311/my-pkg/my-pkg-0.0.1-py3-none-any.msgpack`
           uv_client::cached_client::fresh_request url="https://europe-west1-python.pkg.dev/project/repo/my-pkg/my-pkg-0.0.1-py3-none-any.whl#sha256=5741581bb410a9b09a69871cad4783f7aa84fed6e05e68cb58060ea8d3c994d1"
error: Failed to download: my-pkg==0.0.1
  Caused by: HTTP status client error (401 Unauthorized) for url (https://europe-west1-python.pkg.dev/project/repo/my-pkg/my-pkg-0.0.1-py3-none-any.whl#sha256=5741581bb410a9b09a69871cad4783f7aa84fed6e05e68cb58060ea8d3c994d1)

@GiftofHermes
Copy link

GiftofHermes commented Feb 22, 2024

Same problem here. Using a private azure index with --index-url=https://[personal_access_token]@[private_index_url] using Python 3.9. Strangely it finds the latest package version in the index and when you click on the url in the error message you are able to download the artifact.

This is a redacted version of the verbose output

uv::requirements::from_source source=private-package
    0.001379s DEBUG uv_interpreter::virtual_env Found a virtualenv through VIRTUAL_ENV at: xxxxxxxxxxxxxxxxxxxxxxxxxxxx
    0.001552s DEBUG uv_interpreter::interpreter Using cached markers for: xxxxxxxxxxxxxxxxxxxxxxxxxxxx
    0.001566s DEBUG uv::commands::pip_install Using Python 3.9.5 environment at xxxxxxxxxxxxxxxxxxxxxxxxxxxx
    0.003116s DEBUG uv_client::registry_client Using registry request timeout of 300s
 uv_client::flat_index::from_entries 
 uv_resolver::resolver::solve 
      0.005239s   0ms DEBUG uv_resolver::resolver Solving with target Python version 3.9.5
   uv_resolver::resolver::choose_version package=root
   uv_resolver::resolver::get_dependencies package=root, version=0a0.dev0
        0.005343s   0ms DEBUG uv_resolver::resolver Adding direct dependency: private-package*
   uv_resolver::resolver::choose_version package=private-package
     uv_resolver::resolver::package_wait package_name=private-package
 uv_resolver::resolver::process_request request=Versions private-package
   uv_client::registry_client::simple_api package=private-package
     uv_client::cached_client::get_cacheable 
       uv_client::cached_client::read_and_parse_cache file=/root/.cache/uv/simple-v1/12326308e467ba02/private-package.rkyv
 uv_resolver::resolver::process_request request=Prefetch private-package *
          0.005874s   0ms DEBUG uv_client::cached_client No cache entry for: private-index (without token)
       uv_client::cached_client::fresh_request url="private-index (without token)"
       uv_client::cached_client::new_cache file=/root/.cache/uv/simple-v1/12326308e467ba02/private-package.rkyv
       uv_client::registry_client::parse_simple_api package=private-package
         uv_client::html::parse url=private-index (without token)
 uv_resolver::version_map::from_metadata 
   uv_distribution::distribution_database::get_or_build_wheel_metadata dist=private-package==1.0.0
     uv_client::registry_client::wheel_metadata built_dist=private-package==1.0.0
       uv_client::cached_client::get_serde 
         uv_client::cached_client::get_cacheable 
           uv_client::cached_client::read_and_parse_cache file=/root/.cache/uv/wheels-v0/index/12326308e467ba02/private-package/private-package-1.0.0-py3-none-any.msgpack
        0.648378s 642ms DEBUG uv_resolver::resolver Searching for a compatible version of private-package (*)
        0.648397s 643ms DEBUG uv_resolver::resolver Selecting: private-package==1.0.0 (private_package-1.0.0-py3-none-any.whl)
   uv_resolver::resolver::get_dependencies package=private-package, version=1.0.0
     uv_resolver::resolver::distributions_wait package_id=private-package-1.0.0
              0.648462s   0ms DEBUG uv_client::cached_client No cache entry for: artifact_url
           uv_client::cached_client::fresh_request url=artifact_url
          0.671946s  23ms  WARN uv_client::registry_client Range requests not supported for private_package-1.0.0-py3-none-any.whl; downloading wheel
error: Failed to download: private-package==1.0.0
  Caused by: HTTP status client error (401 Unauthorized) for url (artifact_url)

@zanieb
Copy link
Member

zanieb commented Feb 22, 2024

We're continuing to investigate this. Unfortunately I had no problems authenticating with an access token with a private AWS CodeArtifact repository and haven't set up alternative private registries for testing yet.

@rth
Copy link

rth commented Feb 22, 2024

@zanieb Here a standalone setup to reproduce with simple PEP 503 index and Basic Authentication.

In some folder create,

index.html

<!DOCTYPE html>
<html>
  <body>
    <a href="/non-existing-package/">non-existing-package</a>
  </body>
</html>

/non-existing-package/index.html

<html><head>
    <meta name="pypi:repository-version" content="1.1">
  </head>
  <body>
    <h1>Links for non-existing-package</h1>
<a href="http://127.0.0.1:8080/non-existing-package/non-existing-package-0.1.0.tar.gz#sha256=fb1aa1f8d35a2e3b7bbc2ce632ece9571bb8093dec79826a54be075f15b11ecd">non-existing-package-0.1.0.tar.gz</a><br>

</body></html>

and create this fake package with

touch /non-existing-package/non-existing-package-0.1.0.tar.gz

then run with node,

npx http-server --username user --password password

to get a server with basic auth.

Running uv,

uv pip install non-existing-package==0.1.0 --extra-index-url http://user:[email protected]:8080/ --refresh -v

fails with,

error: Failed to download and build: non-existing-package==0.1.0
  Caused by: HTTP status client error (401 Unauthorized) for url (http://127.0.0.1:8080/non-existing-package/non-existing-package-0.1.0.tar.gz#sha256=fb1aa1f8d35a2e3b7bbc2ce632ece9571bb8093dec79826a54be075f15b11ecd)

So the problem is that this simple API returns absolute URLs for package versions, and the access token is no longer part of the URL hence the 401 error. It works if the URLs are relative.

I'm not sure for other hosting services but Gitlab packages, does return such absolute URLs and consequently fail.

@zanieb
Copy link
Member

zanieb commented Feb 22, 2024

Thank you! Will report back soon.

@rth
Copy link

rth commented Feb 22, 2024

Unless I'm mistaken, the corresponding logic in pip is here https://github.com/pypa/pip/blob/b647ed5782e1fc5627e5e18a036130fea0b413e6/src/pip/_internal/network/auth.py#L404 to account for the fact that the package URL for a private index may not contain credentials.

@charliermarsh
Copy link
Member

These are some high-quality comments, thanks @rth.

@zanieb
Copy link
Member

zanieb commented Feb 22, 2024

A draft fix is up at #1886 — thanks again for the reproduction!

zanieb added a commit that referenced this issue Feb 23, 2024
…les (#1886)

Closes #1709
Closes #1371

Tested with the reproduction provided in #1709 which gets past the HTTP
401.

Reuses the same copying logic we introduced in
#1874 to ensure authentication is
attached to file URLs with a realm that matches that of the index. I had
to move the authentication logic into a new crate so it could be used in
`distribution-types`.

We will want to something more robust in the future, like track all
realms with authentication in a central store and perform lookups there.
That's what `pip` does and it allows consolidation of logic like netrc
lookups. That refactor feels significant though, and I'd like to get
this fixed ASAP so this is a minimal fix.
@AloizioMacedo
Copy link
Author

Thanks for the push!

I installed the package from commit 8a12b2e and it still isn't working for me, same error.

After failure, I created a new env and pip installed uv in editable mode, checked pip list and which uv to triple check that I am using the version of that commit.

@hwong557
Copy link

I am on 0.1.9 and I continue to get a 401.

@charliermarsh
Copy link
Member

@hwong557 - Do you still see missing credentials on HTTP status client error (401 Unauthorized) for url (https://xxx.jfrog.io/yyy/my_private_package1.2.3/my_private_package1.2.3-py3-none-any.whl#sha256=1111111111111111111111111111111111111111111111111111111111111111)?

@charliermarsh
Copy link
Member

Can you also confirm that you cleared the cache beforehand? (uv cache clean)

@hwong557
Copy link

Yes, I cleared the cache, and I get the same missing credentials error.

@charliermarsh
Copy link
Member

@hwong557 - If you open a page like https://xxx.jfrog.io/yyy/simple/my_private_package, are the distribution URLs relative or absolute?

@hwong557
Copy link

hwong557 commented Feb 23, 2024

Sorry for being unhelpful but the page asks for credentials and then refuses my creds. I'll have to talk to someone at my company, but I can confirm that pip-tools works fine.

pip-compile -v pyproject.toml --index-url https://USERNAME:[email protected]/foo/bar --no-emit-index-url

@zanieb zanieb reopened this Feb 23, 2024
@zanieb
Copy link
Member

zanieb commented Feb 23, 2024

Trial JFrog account here I come

@zanieb
Copy link
Member

zanieb commented Feb 23, 2024

We've identified this as a problem preserving authorization headers in streamed range requests.

#1902 should resolve this.

@gwdekker
Copy link

fingers crossed! FWIW same issue on gitlab pypi registry (at uv 0.1.9, after cleaning the cache).

@TheoBabilon
Copy link
Contributor

Was also still getting 401, compiled from zb/fix-range-auth and can confirm that it fixed the issue, thanks a lot @zanieb, can finally see how fast uv is, impressive!

@astrojuanlu

This comment was marked as outdated.

@mpizenberg
Copy link

I just checked out #1902 and I can confirm it now works for GitLab for me

@astrojuanlu
Copy link

I confirm #1902 fixes the issue for JFrog!

@zanieb
Copy link
Member

zanieb commented Feb 23, 2024

Thanks for testing it everyone!

@adrienball
Copy link

adrienball commented Feb 23, 2024

After having installed uv 0.1.9, I still have a 403 Forbidden issue when trying to uv pip install a package hosted on a private artifactory repository.

[EDIT] My bad, #1902 is not part of uv 0.1.9, waiting for the next release 😇

@nitlev
Copy link

nitlev commented Feb 24, 2024

Thanks for fixing it so fast ! Nice to see this projet moving forward as swiftly as it does !

@gwdekker
Copy link

awesome, works now for me as well! Your speed is super impressive, thank you so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment