You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Originally posted by keriksson-rosenqvist September 26, 2022
I am trying to set up a poetry environment which pulls a certain package from a private pypi-like registry. As I am sharing the project with others via a git repository, I want the setup to be contained within the pyproject.toml file, i.e. not rely on configs, environment variables, or keyring settings that may differ between user devices.
I have therefore added a source with the basic http credentials included in the url. The exact same url is able to install the package and it's dependencies through pip install -i https://<username:password>@[REDACTED]/pypi/simple/ my-private-package
I've set secondary = true as that lets dependencies be installed from the normal pypi, otherwise poetry also fails to install them.
According to this issue, others have been seemingly been able to get it working with the credentials in the url, however with the setup above raises the following error for me.
@kristang I've made an MWE using a new hello-world package in my private pipy registry. Running poetry update -vvv gives the following trace:
Loading configuration file [REDACTED]/.config/pypoetry/config.toml
Loading configuration file [REDACTED]/.config/pypoetry/auth.toml
Adding repository hello_world (https://<username:password>@[REDACTED]/hello_world/pypi/simple) and setting it as secondary
Using virtualenv: [REDACTED]/poetry_credential_mwe/.venv
Project environment contains an empty path in sys_path, ignoring.
Updating dependencies
Resolving dependencies...
1: fact: poetry-credential-mwe is 0.1.0
1: derived: poetry-credential-mwe
1: fact: poetry-credential-mwe depends on hello-world (^0.1.0)
1: selecting poetry-credential-mwe (0.1.0)
1: derived: hello-world (>=0.1.0,<0.2.0)
Creating new session for <username:parrword>@[REDACTED]
[urllib3.connectionpool] Starting new HTTPS connection (1): [REDACTED]:443
[urllib3.connectionpool] https://[REDACTED]:443 "GET [REDACTED]/hello_world/pypi/simple/hello-world/ HTTP/1.1" 200 None
Source (hello_world): 1 packages found for hello-world >=0.1.0,<0.2.0
[urllib3.connectionpool] https://[REDACTED]:443 "GET [REDACTED]/hello_world/pypi/simple/hello-world/ HTTP/1.1" 200 None
Source (hello_world): Downloading wheel: hello_world-0.1.0-py3-none-any.whl
[keyring.backend] Loading KWallet
[keyring.backend] Loading SecretService
[keyring.backend] Loading Windows
[keyring.backend] Loading chainer
[keyring.backend] Loading libsecret
[keyring.backend] Loading macOS
Creating new session for [REDACTED]
[urllib3.connectionpool] Starting new HTTPS connection (1): [REDACTED]:443
[urllib3.connectionpool] https://[REDACTED]:443 "GET [REDACTED]/pypi/download/hello-world/0.1/hello_world-0.1.0-py3-none-any.whl HTTP/1.1" 401 343
1: Version solving took 1.522 seconds.
1: Tried 1 solutions.
ValueError
Package('hello-world', '0.1') is not in list
at ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/repositories/legacy_repository.py:51 in package
47β Note that this will be cached so the subsequent operations
48β should be much faster.
49β """
50β try:
β 51β index = self._packages.index(Package(name, version))
52β
53β return self._packages[index]
54β except ValueError:
55β package = super().package(name, version, extras)
The following error occurred when trying to handle this error:
Stack trace:
31 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/cleo/application.py:329 in run
327β
328β try:
β 329β exit_code = self._run(io)
330β except Exception as e:
331β if not self._catch_exceptions:
30 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/console/application.py:185 in _run
183β self._load_plugins(io)
184β
β 185β exit_code: int = super()._run(io)
186β return exit_code
187β
29 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/cleo/application.py:423 in _run
421β io.input.set_stream(stream)
422β
β 423β exit_code = self._run_command(command, io)
424β self._running_command = None
425β
28 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/cleo/application.py:465 in _run_command
463β
464β if error is not None:
β 465β raise error
466β
467β return event.exit_code
27 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/cleo/application.py:449 in _run_command
447β
448β if event.command_should_run():
β 449β exit_code = command.run(io)
450β else:
451β exit_code = ConsoleCommandEvent.RETURN_CODE_DISABLED
26 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/cleo/commands/base_command.py:119 in run
117β io.input.validate()
118β
β 119β status_code = self.execute(io)
120β
121β if status_code is None:
25 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/cleo/commands/command.py:83 in execute
81β
82β try:
β 83β return self.handle()
84β except KeyboardInterrupt:
85β return 1
24 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/console/commands/update.py:54 in handle
52β self.installer.update(True)
53β
β 54β return self.installer.run()
55β
23 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/installation/installer.py:114 in run
112β self._execute_operations = False
113β
β 114β return self._do_install()
115β
116β def dry_run(self, dry_run: bool = True) -> Installer:
22 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/installation/installer.py:247 in _do_install
245β source_root=self._env.path.joinpath("src")
246β ):
β 247β ops = solver.solve(use_latest=self._whitelist).calculate_operations()
248β else:
249β self._io.write_line("Installing dependencies from lock file")
21 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/puzzle/solver.py:73 in solve
71β with self._provider.progress():
72β start = time.time()
β 73β packages, depths = self._solve(use_latest=use_latest)
74β end = time.time()
75β
20 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/puzzle/solver.py:151 in _solve
149β
150β try:
β 151β result = resolve_version(
152β self._package, self._provider, locked=locked, use_latest=use_latest
153β )
19 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/mixology/__init__.py:24 in resolve_version
22β solver = VersionSolver(root, provider, locked=locked, use_latest=use_latest)
23β
β 24β return solver.solve()
25β
18 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/mixology/version_solver.py:127 in solve
125β while next is not None:
126β self._propagate(next)
β 127β next = self._choose_package_version()
128β
129β return self._result()
17 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/mixology/version_solver.py:446 in _choose_package_version
444β package = locked
445β
β 446β package = self._provider.complete_package(package)
447β
448β conflict = False
16 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/puzzle/provider.py:530 in complete_package
528β dependency_package = DependencyPackage(
529β dependency,
β 530β self._pool.package(
531β package.pretty_name,
532β package.version,
15 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/repositories/pool.py:152 in package
150β
151β if repository is not None and not self._ignore_repository_names:
β 152β return self.repository(repository).package(name, version, extras=extras)
153β
154β for repo in self._repositories:
14 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/repositories/legacy_repository.py:55 in package
53β return self._packages[index]
54β except ValueError:
β 55β package = super().package(name, version, extras)
56β package._source_type = "legacy"
57β package._source_url = self._url
13 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/repositories/cached.py:86 in package
84β extras: list[str] | None = None,
85β ) -> Package:
β 86β return self.get_release_info(canonicalize_name(name), version).to_package(
87β name=name, extras=extras
88β )
12 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/repositories/cached.py:63 in get_release_info
61β return PackageInfo.load(self._get_release_info(name, version))
62β
β 63β cached = self._cache.remember_forever(
64β f"{name}:{version}", lambda: self._get_release_info(name, version)
65β )
11 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/cachy/repository.py:174 in remember_forever
172β return val
173β
β 174β val = value(callback)
175β
176β self.forever(key, val)
10 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/cachy/helpers.py:6 in value
4β def value(val):
5β if callable(val):
β 6β return val()
7β
8β return val
9 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/repositories/cached.py:64 in <lambda>
62β
63β cached = self._cache.remember_forever(
β 64β f"{name}:{version}", lambda: self._get_release_info(name, version)
65β )
66β
8 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/repositories/legacy_repository.py:121 in _get_release_info
119β yanked = page.yanked(name, version)
120β
β 121β return self._links_to_data(
122β links,
123β PackageInfo(
7 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/repositories/http.py:256 in _links_to_data
254β data.files = files
255β
β 256β info = self._get_info_from_urls(urls)
257β
258β data.summary = info.summary
6 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/repositories/http.py:193 in _get_info_from_urls
191β # Prefer non platform specific wheels
192β if universal_python3_wheel:
β 193β return self._get_info_from_wheel(universal_python3_wheel)
194β
195β if universal_python2_wheel:
5 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/repositories/http.py:84 in _get_info_from_wheel
82β with temporary_directory() as temp_dir:
83β filepath = Path(temp_dir) / filename
β 84β self._download(url, filepath)
85β
86β return PackageInfo.from_wheel(filepath)
4 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/repositories/http.py:72 in _download
70β
71β def _download(self, url: str, dest: Path) -> None:
β 72β return download_file(url, dest, session=self.session)
73β
74β def _get_info_from_wheel(self, url: str) -> PackageInfo:
3 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/utils/helpers.py:85 in download_file
83β get = requests.get if not session else session.get
84β
β 85β response = get(url, stream=True, timeout=REQUESTS_TIMEOUT)
86β response.raise_for_status()
87β
2 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/utils/authenticator.py:246 in get
244β
245β def get(self, url: str, **kwargs: Any) -> requests.Response:
β 246β return self.request("get", url, **kwargs)
247β
248β def post(self, url: str, **kwargs: Any) -> requests.Response:
1 ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/poetry/utils/authenticator.py:232 in request
230β if resp.status_code not in [502, 503, 504] or is_last_attempt:
231β if raise_for_status:
β 232β resp.raise_for_status()
233β return resp
234β
HTTPError
401 Client Error: Unauthorized for url: https://[REDACTED]/pypi/download/hello-world/0.1/hello_world-0.1.0-py3-none-any.whl#sha256=[REDACTED]
at ~/.local/share/pypoetry/venv/lib/python3.9/site-packages/requests/models.py:1021 in raise_for_status
1017β f"{self.status_code} Server Error: {reason} for url: {self.url}"
1018β )
1019β
1020β if http_error_msg:
β 1021β raise HTTPError(http_error_msg, response=self)
1022β
1023β def close(self):
1024β """Releases the connection back to the pool. Once this method has been
1025β called the underlying ``raw`` object must not be accessed again.
Running poetry run pip install -i https://[REDACTED]/hello_world/pypi/simple hello-worldwithout providing any user credentials also fails with a 401:
$ poetry run pip install -i https://[REDACTED]/hello_world/pypi/simple hello-world
Looking in indexes: https://[REDACTED]/hello_world/pypi/simple
User for [REDACTED]:
WARNING: 401 Error, Credentials not correct for https://[REDACTED]/hello_world/pypi/simple/hello-world/
ERROR: Could not find a version that satisfies the requirement hello-world (from versions: none)
ERROR: No matching distribution found for hello-world
User for [REDACTED]:
WARNING: 401 Error, Credentials not correct for https://[REDACTED]/hello_world/pypi/simple/pip/
Only thing I find a little odd is the url returned: Unauthorized for url: https://[REDACTED]/pypi/download/hello-world/0.1/hello_world-0.1.0-py3-none-any.whl#sha256=[REDACTED]
I don't recall seeing that "/download/" part when I install from a private source. What service is providing your private feed?
It might just be my inexperience with pypi feeds in general.
keriksson-rosenqvist-oqc commented on Oct 17, 2022
I am using Azure Artifacts to store my private feed. As mentioned it works fine with pip install -i on the command line when providing credentials. If I supply the -vvv flag to my pip command it does use that same url https://[REDACTED]/pypi/download/hello-world/0.1/hello_world-0.1.0-py3-none-any.whl#sha256=[REDACTED]
Have you tried other authentication methods? I believe Azure Artifacts supports some kind of API token - last time I checked they had a guide somewhere included on the site for your feed on how to use it.
keriksson-rosenqvist-oqc commented on Oct 18, 2022
@kristang That is what's being used. The tokens are used through the basic auth approach. They have no support for other authentications through pip as far as I have found, and I've been in touch with their support team regarding authentication methods.
This approach worked for me when I was testing Azure Artifacts. There is also support for using a Personal Access Token, which I assume is what you have been trying to do?
Reacted by James Thomas Moon and Danila Ganchar
keriksson-rosenqvist-oqc commented on Oct 18, 2022
A keyring would not solve the problem I've defined:
I am trying to set up a poetry environment which pulls a certain package from a private pypi-like registry. As I am sharing the project with others via a git repository, I want the setup to be contained within the pyproject.toml file, i.e. not rely on configs, environment variables, or keyring settings that may differ between user devices.
With regards to the API token versus PAT, the documentation shows that to use the pip interface you must use a PAT. I have not found any documentation on another form of token that could be used for this.
@squirrel532 with version 1.1.15 poetry lock works, but installation still fails for me.
It's interesting that the authentication seems to work in the first step. Depending on which version I set in the pyproject.toml (e.g. ^0.4.0, <=0.4.0) it is able to figure out which package it should install including the correct download link. But then in the second step when it comes to downloading the package, the authentication fails.
@bfontaine I think it is risky that someone places their credentials in pyproject.toml. Maybe we just places a warning in document to prohibit this behavior ?
I donβt see any technical reason this shouldnβt work. Right now this is a bug: the URL including credentials is perfectly valid but Poetry appears to silently strip the credentials.
Itβs risky for public packages, but there are valid use-cases in a private/company environment. See what @keriksson-rosenqvist wrote in their initial message:
I want the setup to be contained within the pyproject.toml file, i.e. not rely on configs, environment variables, or keyring settings that may differ between user devices.
Hi! I also experience this issue. Thanks to @squirrel532 for this work around. So we can keep our internal projects going.
I think, people should be careful with their credentials and think twice before putting them anywhere. But in our internal projects at work it is quite useful to checkout private repos which are dependent on each other.
So if I understood it correctly the bug is still there but will not be fixed as the issue was closed? π€
I cannot imagine the project will ever accept credentials in pyproject.toml, poetry has no idea where that pyproject.toml will be published, in public or private. The first project that accidentally publishes credentials in pyproject.toml will end up with a security bug against poetry.
@clintonroy The question is not if the project accepts credentials because it doesnβt have a choice: URLs that contain credentials are valid HTTP URLs that shouldnβt be dealt with in a different manner than others. The suggested solution is to put the credentials in poetry.toml, which you can put in git as well. I donβt remember seeing a security bug against SSH because someone put their private key in a public Git repository.
@fannigurt yes, but this issue is specifically about adding credentials inside the repository in order to share them with everyone. By default poetry config edits your user config. To edit the local config, you can use poetry config --local and get the same result as the workaround mentioned earlier.
This issue is still present in Poetry 1.5.1, and I believe that I've gotten to the bottom of it.
In my testing with Google Artifact Registry (GAR), I'm seeing that the simple repository base URL is returning href values with absolute URLs. In my testing, if relative URLs are returned, there is no issue. I believe that it is likely that everyone experiencing this issue is using a repository which returns absolute URLs.
<!DOCTYPE html><html><head><title> Links for [REDACTED] </title></head><body><h1> Links for [REDACTED] </h1><ahref="https://us-east1-python.pkg.dev/[REDACTED]-py3-none-any.whl#sha256=deadbeef">[REDACTED]-py3-none-any.whl</a><br/><ahref="https://us-east1-python.pkg.dev/[REDACTED].tar.gz#sha256=feedface">[REDACTED].tar.gz</a><br/></body></html>
URLs may be either absolute or relative as long as they point to the correct location.
So there's nothing invalid about GAR's response.
However, within poetry, this does not play nicely with the HTTP basic authentication credentials. When poetry processes these responses, urllib.parse.urljoin is used to combine the source's authenticated URL with the non-authenticated URLs from the HTML above.
7486 is perhaps the best solution here, as it would intercept the credentials into Poetry's authentication system early on before they have a chance to get mangled.
However, there are some other potential risks caused by the same issue. For example, a user could be connecting to a repository via a proxy, and the returned absolute path would override the proxy URL.
TLDR: check your ~/.netrc file for conflicting credentials.
I think I am having the same/similar issue. Unfortunately the workarounds did not work in my case. I believe in my case the error has to do with my environment (i.e. the installed packages, libraries, etc.). I'm on Ubuntu 22.04 using Poetry 1.6.1. AFAIK I haven't made any changes to poetry credential configuration between the time everything was working (~1 week ago) and now.
I have traced my error to my system because (after a lot of troubleshooting), I found that running poetry commands (lock, update, install) in a docker container and using a secret mount to bind a local auth.toml to ~/.config/pypoetry/auth.toml in the container works to authenticate and pull the packages from the private repo. Also, putting the credentials directly into the source URL works but due to the behavior described in #6799 (comment) (as well as the obvious security risks with storing secrets in distributable code) this is not a full workaround.
Therefore I know that a) the credentials are valid and b) on a "clean" system the process works. At some point some package or library poetry uses to authenticate must have gotten corrupted in my local system however attempts to hunt this package down have all failed - downgrading poetry, force installing python and system packages like keyring, urllib, python-dbus, libdbus, etc. have all failed to solve the problem.
Additionally disabling the keyring entirely and either falling back to plaintext or environment variable authentication does not work either. Unfortunately, due to the nature of the problem I cannot produce a reproduction script since I have no idea which library on my system has become "corrupted". I'm hoping someone can at least point me in a direction to look.
Any help would be greatly appreciated. In the meantime I am about to run the poetry lock command through a debugger to introspect what exact requests and metadata are being sent/received so hopefully I can isolate whether the issue lies in poetrys ability to retrieve the credentials from the keyring/plaintext/environment or a malformed request itself.
UPDATE 2023-10-24
I've been able to rule out poetry not getting the credentials from the keyring - it gets the credentials, encrypts them, and places them into the header. I haven't checked if the encryption is correct yet. The content of the response claims the token has expired but this doesn't make sense as the repository maintainers checked this and confirmed it was valid. Furthermore the issue occurs with my personal username and token too (I have developer access to the repository).
Just reinstalled requests and cryptography too.
Okay - I finally traced the error to conflicting credentials in ~/.netrc vs the keyring. Apparently, poetry will use requests to get credentials for the repository (see here) which uses ~/.netrc credentials to form the encoded authorization in the header of the request. So any changes to the configuration of the credentials using poetry config, environment variables, etc. is essentially discarded for authenticating the actual request in favor of the contents of ~/.netrc
SO if you come across this issue and are in a situation like mine - where none of the above workarounds fixed anything - check your .netrc file for configurations matching the repository URL and either remove them or modify them so poetry uses the correct credentials.
Reacted by Squirrel, James Thomas Moon, JoΓ£o Neves, josemonteiro, Kristian Eschenburg, ksonj, Valeriy Kuzmin, bruceduhamel and Krishna Tippur
I think I am having the same/similar issue. Unfortunately the workarounds did not work in my case. I believe in my case the error has to do with my environment (i.e. the installed packages, libraries, etc.). I'm on Ubuntu 22.04 using Poetry 1.6.1. AFAIK I haven't made any changes to poetry credential configuration between the time everything was working (~1 week ago) and now.
I have traced my error to my system because (after a lot of troubleshooting), I found that running poetry commands (lock, update, install) in a docker container and using a secret mount to bind a local auth.toml to ~/.config/pypoetry/auth.toml in the container works to authenticate and pull the packages from the private repo. Also, putting the credentials directly into the source URL works but due to the behavior described in #6799 (comment) (as well as the obvious security risks with storing secrets in distributable code) this is not a full workaround.
Therefore I know that a) the credentials are valid and b) on a "clean" system the process works. At some point some package or library poetry uses to authenticate must have gotten corrupted in my local system however attempts to hunt this package down have all failed - downgrading poetry, force installing python and system packages like keyring, urllib, python-dbus, libdbus, etc. have all failed to solve the problem.
Additionally disabling the keyring entirely and either falling back to plaintext or environment variable authentication does not work either. Unfortunately, due to the nature of the problem I cannot produce a reproduction script since I have no idea which library on my system has become "corrupted". I'm hoping someone can at least point me in a direction to look.
Any help would be greatly appreciated. In the meantime I am about to run the poetry lock command through a debugger to introspect what exact requests and metadata are being sent/received so hopefully I can isolate whether the issue lies in poetrys ability to retrieve the credentials from the keyring/plaintext/environment or a malformed request itself.
UPDATE 2024-10-24
I've been able to rule out poetry not getting the credentials from the keyring - it gets the credentials, encrypts them, and places them into the header. I haven't checked if the encryption is correct yet. The content of the response claims the token has expired but this doesn't make sense as the repository maintainers checked this and confirmed it was valid. Furthermore the issue occurs with my personal username and token too (I have developer access to the repository).
Just reinstalled requests and cryptography too.
Okay - I finally traced the error to conflicting credentials in ~/.netrc vs the keyring. Apparently, poetry will use requests to get credentials for the repository (see here) which uses ~/.netrc credentials to form the encoded authorization in the header of the request. So any changes to the configuration of the credentials using poetry config, environment variables, etc. is essentially discarded for authenticating the actual request in favor of the contents of ~/.netrc
SO if you come across this issue and are in a situation like mine - where none of the above workarounds fixed anything - check your .netrc file for configurations matching the repository URL and either remove them or modify them so poetry uses the correct credentials.
It is 2023-10-24, not 2024
Reacted by Baptiste Fontaine, JoΓ«l Perras, gresavage and James Thomas Moon
This completely bypass the pypi server but there seems to be no other choice while this issue is not fixed.
woow, if you create default username for token, which is gitlab+deploy-token-{n}, poetry will consider it as a wrong git url. I advice to create simple usernames like user. Thanks for an answer, that helped!
I was having this same issue. Turns out I had my Gitlab credentials stored in the .netrc file as well, so @gresavage solution solved the failed authorization issues for me.
I had a similar issue with GitLab. Since the package I wanted to install was in another repo in GitLab, I had to go to that repo's Settings > CI/CD and add my other repo (the one where the pipelines run) under Token Access > Limit access to this project > Groups and projects with access. More info.
After that, I could use Poetry to add my private package with a pipeline job token.
Activity
kristang commentedon Oct 14, 2022
Try running
poetry update -vvvand post the output (remember to redact anything you don't want public - like your feed address)Does
pip install -i https://[REDACTED]/pypi/simple/my-private-packagefail with a 401?keriksson-rosenqvist-oqc commentedon Oct 17, 2022
@kristang I've made an MWE using a new
hello-worldpackage in my private pipy registry. Runningpoetry update -vvvgives the following trace:Running
poetry run pip install -i https://[REDACTED]/hello_world/pypi/simple hello-worldwithout providing any user credentials also fails with a 401:kristang commentedon Oct 17, 2022
Only thing I find a little odd is the url returned:
Unauthorized for url: https://[REDACTED]/pypi/download/hello-world/0.1/hello_world-0.1.0-py3-none-any.whl#sha256=[REDACTED]I don't recall seeing that "/download/" part when I install from a private source. What service is providing your private feed?
It might just be my inexperience with pypi feeds in general.
keriksson-rosenqvist-oqc commentedon Oct 17, 2022
I am using Azure Artifacts to store my private feed. As mentioned it works fine with
pip install -ion the command line when providing credentials. If I supply the-vvvflag to my pip command it does use that same urlhttps://[REDACTED]/pypi/download/hello-world/0.1/hello_world-0.1.0-py3-none-any.whl#sha256=[REDACTED]kristang commentedon Oct 17, 2022
Have you tried other authentication methods? I believe Azure Artifacts supports some kind of API token - last time I checked they had a guide somewhere included on the site for your feed on how to use it.
keriksson-rosenqvist-oqc commentedon Oct 18, 2022
@kristang That is what's being used. The tokens are used through the basic auth approach. They have no support for other authentications through pip as far as I have found, and I've been in touch with their support team regarding authentication methods.
kristang commentedon Oct 18, 2022
According to the documentation from Microsoft you can auth with
artifacts-keyring.You can then install with:
https://learn.microsoft.com/en-us/azure/devops/artifacts/quickstarts/python-packages?view=azure-devops#use-artifacts-keyring-to-set-up-authentication
This approach worked for me when I was testing Azure Artifacts. There is also support for using a Personal Access Token, which I assume is what you have been trying to do?
keriksson-rosenqvist-oqc commentedon Oct 18, 2022
A keyring would not solve the problem I've defined:
With regards to the API token versus PAT, the documentation shows that to use the
pipinterface you must use a PAT. I have not found any documentation on another form of token that could be used for this.kristang commentedon Oct 19, 2022
Arh, sorry I completely skipped that.
(Off-topic and sidenote: I would be careful about storing your credentials like that in your git)
squirrel532 commentedon Nov 1, 2022
I'm facing similar problem which happened on a self-host GitLab PyPI repo too.
On version
1.2.2,poetry lockfailed, but it works on version1.1.15.keriksson-rosenqvist-oqc commentedon Nov 1, 2022
I'm currently running version 1.2.1, but if this used to work in an older version I'd say it's definitely a bug that it's not working now.
manulen commentedon Nov 10, 2022
same problem here..
@squirrel532 with version 1.1.15
poetry lockworks, but installation still fails for me.It's interesting that the authentication seems to work in the first step. Depending on which version I set in the pyproject.toml (e.g. ^0.4.0, <=0.4.0) it is able to figure out which package it should install including the correct download link. But then in the second step when it comes to downloading the package, the authentication fails.
keriksson-rosenqvist-oqc commentedon Dec 8, 2022
Is there any further update on this? Can it be tagged as a bug please?
squirrel532 commentedon Jan 21, 2023
As a workaround, I configured
pipwith.netrc, which contains the credentials of my private repo.https://pip.pypa.io/en/stable/topics/authentication/#netrc-support
9 remaining items
squirrel532 commentedon Feb 7, 2023
@bfontaine I think it is risky that someone places their credentials in
pyproject.toml. Maybe we just places a warning in document to prohibit this behavior ?CC @sdispater
bfontaine commentedon Feb 7, 2023
I donβt see any technical reason this shouldnβt work. Right now this is a bug: the URL including credentials is perfectly valid but Poetry appears to silently strip the credentials.
Itβs risky for public packages, but there are valid use-cases in a private/company environment. See what @keriksson-rosenqvist wrote in their initial message:
Strice91 commentedon Feb 7, 2023
Hi! I also experience this issue. Thanks to @squirrel532 for this work around. So we can keep our internal projects going.
I think, people should be careful with their credentials and think twice before putting them anywhere. But in our internal projects at work it is quite useful to checkout private repos which are dependent on each other.
So if I understood it correctly the bug is still there but will not be fixed as the issue was closed? π€
keriksson-rosenqvist-oqc commentedon Feb 8, 2023
Since there seems to be more people experiencing this issue, I've reopened it in hopes the bug will be looked at in the future.
clintonroy commentedon Feb 8, 2023
I cannot imagine the project will ever accept credentials in pyproject.toml, poetry has no idea where that pyproject.toml will be published, in public or private. The first project that accidentally publishes credentials in pyproject.toml will end up with a security bug against poetry.
bfontaine commentedon Feb 9, 2023
@clintonroy The question is not if the project accepts credentials because it doesnβt have a choice: URLs that contain credentials are valid HTTP URLs that shouldnβt be dealt with in a different manner than others. The suggested solution is to put the credentials in
poetry.toml, which you can put in git as well. I donβt remember seeing a security bug against SSH because someone put their private key in a public Git repository.fannigurt commentedon Mar 20, 2023
Hi!, thanks a lot for the instructions above!
Just wanted to mention, that we can add credentials without creating additional
poetry.tomllocally.Here is the config command:
So such steps need:
Add credentials as mentioned
pyproject.tomledit like squirrel532 provided above:Use
poetry add you_awesome_packageorpoetry update you_awesome_packageas usual.bfontaine commentedon Mar 20, 2023
@fannigurt yes, but this issue is specifically about adding credentials inside the repository in order to share them with everyone. By default
poetry configedits your user config. To edit the local config, you can usepoetry config --localand get the same result as the workaround mentioned earlier.amp-matthew-wallace commentedon Aug 1, 2023
This issue is still present in Poetry
1.5.1, and I believe that I've gotten to the bottom of it.In my testing with Google Artifact Registry (GAR), I'm seeing that the simple repository base URL is returning
hrefvalues with absolute URLs. In my testing, if relative URLs are returned, there is no issue. I believe that it is likely that everyone experiencing this issue is using a repository which returns absolute URLs.PEP 503 states:
So there's nothing invalid about GAR's response.
However, within poetry, this does not play nicely with the HTTP basic authentication credentials. When poetry processes these responses,
urllib.parse.urljoinis used to combine the source's authenticated URL with the non-authenticated URLs from the HTML above.poetry/src/poetry/repositories/link_sources/html.py
Line 35 in dfd0db8
urljoindrops the credentials when joining the two URLs.Produces:
https://us-east1-python.pkg.dev/path/to/repo/[REDACTED].tar.gz#sha256=feedface7486 is perhaps the best solution here, as it would intercept the credentials into Poetry's authentication system early on before they have a chance to get mangled.
However, there are some other potential risks caused by the same issue. For example, a user could be connecting to a repository via a proxy, and the returned absolute path would override the proxy URL.
Produces:
https://us-east1-python.pkg.dev/path/to/repo/[REDACTED].tar.gz#sha256=feedfacegresavage commentedon Oct 24, 2023
TLDR: check your
~/.netrcfile for conflicting credentials.I think I am having the same/similar issue. Unfortunately the workarounds did not work in my case. I believe in my case the error has to do with my environment (i.e. the installed packages, libraries, etc.). I'm on Ubuntu 22.04 using Poetry 1.6.1. AFAIK I haven't made any changes to poetry credential configuration between the time everything was working (~1 week ago) and now.
I have traced my error to my system because (after a lot of troubleshooting), I found that running poetry commands (
lock,update,install) in a docker container and using a secret mount to bind a localauth.tomlto~/.config/pypoetry/auth.tomlin the container works to authenticate and pull the packages from the private repo. Also, putting the credentials directly into the source URL works but due to the behavior described in #6799 (comment) (as well as the obvious security risks with storing secrets in distributable code) this is not a full workaround.Therefore I know that a) the credentials are valid and b) on a "clean" system the process works. At some point some package or library poetry uses to authenticate must have gotten corrupted in my local system however attempts to hunt this package down have all failed - downgrading poetry, force installing python and system packages like keyring, urllib, python-dbus, libdbus, etc. have all failed to solve the problem.
Additionally disabling the keyring entirely and either falling back to plaintext or environment variable authentication does not work either. Unfortunately, due to the nature of the problem I cannot produce a reproduction script since I have no idea which library on my system has become "corrupted". I'm hoping someone can at least point me in a direction to look.
Any help would be greatly appreciated. In the meantime I am about to run the
poetry lockcommand through a debugger to introspect what exact requests and metadata are being sent/received so hopefully I can isolate whether the issue lies in poetrys ability to retrieve the credentials from the keyring/plaintext/environment or a malformed request itself.UPDATE 2023-10-24
I've been able to rule out poetry not getting the credentials from the keyring - it gets the credentials, encrypts them, and places them into the header. I haven't checked if the encryption is correct yet. The content of the response claims the token has expired but this doesn't make sense as the repository maintainers checked this and confirmed it was valid. Furthermore the issue occurs with my personal username and token too (I have developer access to the repository).
Just reinstalled requests and cryptography too.
Okay - I finally traced the error to conflicting credentials in
~/.netrcvs the keyring. Apparently,poetrywill userequeststo get credentials for the repository (see here) which uses~/.netrccredentials to form the encoded authorization in the header of the request. So any changes to the configuration of the credentials usingpoetry config, environment variables, etc. is essentially discarded for authenticating the actual request in favor of the contents of~/.netrcSO if you come across this issue and are in a situation like mine - where none of the above workarounds fixed anything - check your
.netrcfile for configurations matching the repository URL and either remove them or modify them so poetry uses the correct credentials.ustcxmwu commentedon Jan 5, 2024
It is 2023-10-24, not 2024
waryak commentedon Feb 2, 2024
woow, if you create default username for token, which is
gitlab+deploy-token-{n}, poetry will consider it as a wrong git url. I advice to create simple usernames likeuser. Thanks for an answer, that helped!kristianeschenburg commentedon Jun 11, 2024
I was having this same issue. Turns out I had my Gitlab credentials stored in the .netrc file as well, so @gresavage solution solved the failed authorization issues for me.
emocibob commentedon Jul 26, 2024
I had a similar issue with GitLab. Since the package I wanted to install was in another repo in GitLab, I had to go to that repo's Settings > CI/CD and add my other repo (the one where the pipelines run) under Token Access > Limit access to this project > Groups and projects with access. More info.
After that, I could use Poetry to add my private package with a pipeline job token.