poetry: Authentication from poetry.toml doesn't work on 1.1.x

  • I am on the latest Poetry version.

  • I have searched the issues of this repo and believe that this is not a duplicate.

  • If an exception occurs when executing a command, I executed it again in debug mode (-vvv option).

  • OS version and name: MacOS 10.15.7

  • Poetry version: 1.1.3

Issue

After upgrade from 1.0.10 to 1.1.3 the authentication for private repositories doesn’t work.

I have an file poetry.toml with this content:

[repositories]
[repositories.MyPrivateFeed]
url = "https://my-private-repo/pypi/simple/"

[http-basic]
[http-basic.MyPrivateFeed]
username = "user"
password = "pass"

And on pyproject.toml

  [[tool.poetry.source]]
  name = "MyPrivateFeed"
  url = "https://my-private-repo/pypi/simple/"
  secondary = true

When I try to update/lock/install I get an error:

update

poetry update

Updating dependencies
Resolving dependencies... (0.4s)

  RepositoryError

  401 Client Error: Unauthorized for url: https://my-private-repo/pypi/simple/mypy/

  at ~/.pyenv/versions/3.8.5/envs/my-project/lib/python3.8/site-packages/poetry/repositories/legacy_repository.py:390 in _get
      386│             if response.status_code == 404:
      387│                 return
      388│             response.raise_for_status()
      389│         except requests.HTTPError as e:
    → 390│             raise RepositoryError(e)
      391│
      392│         if response.status_code in (401, 403):
      393│             self._log(
      394│                 "Authorization error accessing {url}".format(url=url), level="warn"

lock

poetry lock

Updating dependencies
Resolving dependencies... (0.4s)

  RepositoryError

  401 Client Error: Unauthorized for url: https://my-private-repo/pypi/simple/mypy/

  at ~/.pyenv/versions/3.8.5/envs/my-project/lib/python3.8/site-packages/poetry/repositories/legacy_repository.py:390 in _get
      386│             if response.status_code == 404:
      387│                 return
      388│             response.raise_for_status()
      389│         except requests.HTTPError as e:
    → 390│             raise RepositoryError(e)
      391│
      392│         if response.status_code in (401, 403):
      393│             self._log(
      394│                 "Authorization error accessing {url}".format(url=url), level="warn"

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 44
  • Comments: 41 (13 by maintainers)

Most upvoted comments

Why this issue is closed? It seems like the fix has not been pushed

I’m also experiencing this same issue

I had the same issue, it works for me when I do the following steps:

  1. In the file pyproject.toml remove the word simple from the url in [[tool.poetry.source]]
  2. make sure that you run poetry config repositories <REPOSITORY_NAME> <URL> where url is the same one that you set in [[tool.poetry.source]] (without the word simple)

I also dont understand why this ticket was closed. I had exactly the same problem… In fact, because of this Keyring issue, when installing the public packages, poetry even starts looking first for my private repo, so I started specifying in each dependency, the repo source:

[tool.poetry.dependencies]
python = "^3.9"
asyncio-mqtt = "^0.11.0"
environs = "^9.3.5"
mqtt-api = { version = "^0.5.0", source = "pypi-private"}

[tool.poetry.dev-dependencies]
pytest =  { version = "^6.2.5", source = "pypi"}
pytest-lazy-fixture = { version ="^0.6.3", source = "pypi"}
...

And just after this process, the library tells me the problem is really with the authentication with the private server.

After following the steps provided by @SimonVerhoek everything worked as expected. Also, if one specifies directly in the toml the credentials like this:

[[tool.poetry.source]]
name = "pypi-private"
url = "https://username@password:pypi-private.com/simple/"
default = false
secondary = true

it also works, but this is not ideal as no one wants the credentials to be plain text in github.

Another fun fact: I didnt experience this problem neither in MacOS or Docker container. Was really only when I tried in a Linux virtual machine 😕

After trying out everything stated here multiple times without avail and banging my head against the wall over the course of a few days, I finally seem to have a situation working where a poetry project can import a self-made private package published with poetry.

While looking at the auth.toml, I noticed that no password was set. It looked like this:

[http-basic.my-package]
username = "<personal_access_token>"

Apparently, the password is set in your OS’s native keychain by the library called keyring by default. I did the following:

  • switch off keyring like this
  • rerun poetry config http-basic.my-package <personal_access_token> <personal_access_token_password>. This now gave the following messages in return:
No suitable keyring backends were found
Using a plaintext file to store and retrieve credentials

Poetry’s auth.toml now looked like this:

[http-basic.my-package]
username = "<personal_access_token>"
password = "<personal_access_token_password>"

…and after running poetry update once more, the library installed flawlessly.

This worked with poetry==1.1.11, installed by brew on MacOS. (part of) my pyproject.toml:

[[tool.poetry.source]]
name = "my-package"
url = "https://gitlab.com/api/v4/projects/<project_no>/packages/pypi/simple/"
secondary = true

[tool.poetry.dependencies]
my-package = { version = "^<your.package.version>", source = "my-package" }

Another data point: I could not get @ayalganem55’s steps to work. But what seemed to do the trick was to set default = true for the private source. In our case, the private source is an Artifactory which includes a pypi mirror. Preliminary testing suggests that neither setting poetry config repositories nor removing simple from the URL are required as long as default = true is set. I’m using poetry version 1.1.4.

In pyproject.toml:

[[tool.poetry.source]]
name = "mysource"
url = "https://artifactory.com/artifactory/api/pypi/pypi/simple"
default = true

Local configuration: poetry config http-basic.mysource <user> <password>

Well, this issue is closed, but I found my issue which I think may actually be different. I had http-basic setup for the repository for the purpose of posting to it. For our artifactory pypi repository authorization is not needed to get from the repository, only to publish. Therefore, it did not even occur to me that poetry may be using the basic auth credentials for get operations. However, I changed my password so auth failed even for get. I guess that kind of makes sense because poetry doesn’t know how the security is configured on your repo so if you provide a user name and password it just uses it for all operations. Your user name and password should be valid. It just took me longer to troubleshoot because the particular operation I was performing did not actually require authorization so I forgot to check if the credentials themselves are valid. I guess @marcosborges we both solved our respective problems. However, maybe this will help the next person.

I am facing authentication issues with a private repo that doesn’t require authentication. any guidance on how to solve this? 401 Client Error: Unauthorized for url: XXX

Thank you @SimonVerhoek , your steps helped me solve this problem when trying to download packages from AWS CodeArtifact. We are building container images and using Docker secrets to mount the auth.conf from the host temporarily into the build environment, which is incompatible with using the OS keyring.

I opted to configure the Python keyring with environment variables in our build scripts like this:

export PYTHON_KEYRING_BACKEND=keyring.backends.fail.Keyring
poetry config http-basic.codeartifact aws "$(
  aws codeartifact get-authorization-token --domain <domain> \
  --query 'authorizationToken' --output text
  )"

I had this issue recently with AWS CodeArtifact and what solved my problem was using these environment variables: POETRY_HTTP_BASIC_ARTIFACTORY_<AWS_CODEARTIFACT_REPO_NAME>_USERNAME POETRY_HTTP_BASIC_ARTIFACTORY_<AWS_CODEARTIFACT_REPO_NAME>_PASSWORD The last one having the same value of CODEARTIFACT_TOKEN Um abraço @jairhenrique !

I am also experiencing the same issue.

$ poetry config http-basic.fury "<test>" NOPASS

$ cat "/Users/lilja/Library/Application Support/pypoetry/auth.toml"
[http-basic]
[http-basic.fury]
username = "<test>"
$ poetry config http-basic.fury -vvv
Loading configuration file /Users/lilja/Library/Application Support/pypoetry/config.toml
Loading configuration file /Users/lilja/Library/Application Support/pypoetry/auth.toml

  Stack trace:

  4  ~/.poetry/lib/poetry/_vendor/py3.8/clikit/console_application.py:131 in run
      129│             parsed_args = resolved_command.args
      130│
    → 131│             status_code = command.handle(parsed_args, io)
      132│         except KeyboardInterrupt:
      133│             status_code = 1

  3  ~/.poetry/lib/poetry/_vendor/py3.8/clikit/api/command/command.py:120 in handle
      118│     def handle(self, args, io):  # type: (Args, IO) -> int
      119│         try:
    → 120│             status_code = self._do_handle(args, io)
      121│         except KeyboardInterrupt:
      122│             if io.is_debug():

  2  ~/.poetry/lib/poetry/_vendor/py3.8/clikit/api/command/command.py:171 in _do_handle
      169│         handler_method = self._config.handler_method
      170│
    → 171│         return getattr(handler, handler_method)(args, io, self)
      172│
      173│     def __repr__(self):  # type: () -> str

  1  ~/.poetry/lib/poetry/_vendor/py3.8/cleo/commands/command.py:92 in wrap_handle
       90│         self._command = command
       91│
    →  92│         return self.handle()
       93│
       94│     def handle(self):  # type: () -> Optional[int]

  ValueError

  There is no http-basic.fury setting.

  at ~/.poetry/lib/poetry/console/commands/config.py:128 in handle
      124│                 self.line(str(value))
      125│             else:
      126│                 values = self.unique_config_values
      127│                 if setting_key not in values:
    → 128│                     raise ValueError("There is no {} setting.".format(setting_key))
      129│
      130│                 value = config.get(setting_key)
      131│
      132│                 if not isinstance(value, basestring):

I don’t know if this is the proper fix, but I manually edited poetry/console/commands/config.py: image

Commenting out the raise ValueError fixed it for me.

A temporary solution that works for me is adding the private repository via

poetry config repositories.<name> <url>