January 1, 2024·6 min read·engineeringpythonpypisecuritypublishing

PyPI makes two-factor authentication a publishing requirement

PyPI now requires two-factor authentication before account management and uploads. The rollout also separates human login from the credentials used by release automation.

Protecting a package maintainer's browser login leaves an obvious gap if the same password still authorizes uploads. PyPI has spent several years closing that gap while providing a workable alternative for automated releases. Its January 1 announcement completes the account policy rollout. Users must enable two-factor authentication before performing management actions or uploading files. After enrollment, they can generate API tokens or configure Trusted Publishers, which PyPI prefers for uploads.

The change matters because a package registry delegates publication authority to individual accounts. An installer cannot assess the circumstances under which a maintainer authenticated. It receives the resulting distribution. Strengthening the account that controls publication therefore protects consumers who never interact with that account themselves. This is an authorization dependency between the person maintaining a library and everyone accepting its next release.

I choose Python largely because its libraries have already solved problems that would otherwise require substantial work. Its packaging systems are also the part of the language ecosystem I find most frustrating. A requirement that makes publication authority more explicit is worthwhile, even when it introduces another administrative step. The useful question is whether that step addresses a specific failure mode and leaves automation with a coherent authentication model.

Downloads do not require enrollment

The December enforcement notice separates package consumption from account management. Browsing, downloading, and installing packages do not require a PyPI account. Existing users who already have 2FA enabled need no new enrollment. Users without it can still log in and browse, but management actions redirect them toward setup. Upload attempts can report that an API token or Trusted Publisher is required.

That distinction makes the practical scope easier to understand. The policy governs the authority to alter a project or publish its files. It does not introduce an interactive authentication requirement into every dependency installation. A build that only downloads public packages is outside this enrollment path. A release process that publishes packages has to satisfy it through the account and upload mechanisms appropriate to that process.

The notice also places responsibility on whoever currently controls publication. A dormant account may still represent an active project's release authority. Forwarding the announcement to the current maintainer is therefore more useful than assuming that an infrequently used login no longer matters. Authentication policy follows the account's permissions, including permissions exercised only during occasional releases.

The original implementation protected browser sessions

The May 2019 introduction added time-based one-time passwords as an optional second factor. An enrolled user supplied a generated code alongside their username and password when logging in. That implementation required a verified primary email address before setup. It protected website operations such as project ownership changes and deletion of older releases. Package uploads initially continued without a second-factor code.

That initial separation explains why today's requirement needed more than a switch in the login form. Human authentication and unattended publication have different operating conditions. A maintainer can respond to a prompt during a browser session. A release job needs an authentication mechanism that can run without the person being present. Retaining the account password in that job would preserve a separate route through which a stolen password could authorize publication.

PyPI added another browser authentication option in June 2019, when it introduced WebAuthn support for compatible security keys. The announcement described hardware communicating through USB, NFC, or Bluetooth and a browser prompting the user to activate the key. It still described uploads as a separate problem, with application-specific tokens under development. The distinction between interactive authentication and upload authentication was present from the beginning of the work.

Upload tokens reduce the authority carried by a credential

The PSF's January 2020 account-security announcement explains why API tokens are preferable to reusing a login password in release tooling. A token authorizes package uploads without providing general website login. Copying that token does not also grant the ability to remove collaborators or delete a project through the website. Tokens can cover the account's projects or be limited to one project, allowing a compromised project token to be revoked independently.

Those properties narrow what a stored credential can do. A project-scoped token still grants meaningful publication authority, so its theft remains serious. The improvement is that its permissions need not match every administrative permission of the human account. Separating those permissions makes the consequences of credential disclosure more specific and makes replacement less entangled with the maintainer's ordinary login.

The 2020 announcement also anticipated enforcing token use for accounts with 2FA. That enforcement arrived on June 1, 2023. Accounts with 2FA enabled then had to upload using an API token or a Trusted Publisher configuration. Password-only upload authentication was no longer an acceptable alternative for those accounts. The notice says PyPI had begun warning uploaders in February 2022 and had delayed enforcement over concerns about user-scoped tokens for creating new projects.

That delay is useful context for the eventual mandate. Enforcing a stronger browser login without resolving project creation and automated uploads would have left maintainers with an incomplete workflow. The June announcement identifies Trusted Publishing as the mechanism that allowed new projects to be published without provisioning a long-lived user-scoped token. Authentication policy became easier to enforce once the alternative covered that operation.

Trusted Publishing changes what automation stores

PyPI introduced Trusted Publishers in April 2023. A maintainer configures PyPI to trust an identity from an OpenID Connect provider. The automated job presents that identity and exchanges it for a short-lived, narrowly scoped PyPI upload token. The release system no longer needs a manually generated PyPI password or long-lived API token stored as a secret.

The introduction supported GitHub Actions and showed how an existing publishing job could change. After configuring the matching repository and workflow on PyPI, the job grants id-token: write permission and removes the action's explicit username and password inputs. The PyPA publishing action performs the exchange. PyPI also recommends binding the publisher to a GitHub environment, where additional release restrictions can apply.

This arrangement changes the credential-management problem. A persistent upload secret can be copied and retained independently of the job that normally uses it. A federated workflow instead depends on the configured external identity and the short-lived exchange. The configuration defining that identity consequently becomes part of publication authorization. Removing a stored secret does not remove the need to protect the workflow that can obtain upload authority.

I prefer this model because it makes the release system's identity part of the registry configuration. Python packaging already demands too much coordination between separate tools. Avoiding another persistent secret reduces one piece of that coordination without pretending that the workflow itself becomes trustworthy automatically. The permissions attached to that workflow still determine who can cause it to publish.

A small dependency still needs a protected maintainer

The May 2023 policy explanation argues for extending protection beyond the most downloaded projects. A consumer only needs one compromised dependency to be affected. Download popularity therefore provides an incomplete basis for deciding whose accounts deserve protection. That argument also explains why the policy applies across project maintainers despite the uneven value of their accounts to an attacker.

The same explanation acknowledges the cost. Users must manage an additional factor, and PyPI must support people who lose access. Those support obligations are part of operating stronger authentication. They do not disappear because the policy is justified. The rollout's pacing reflects a service that has to improve security while remaining usable by the people publishing its contents.

For a package consumer, the benefit is indirect. Enrollment happens at the maintainer's account, while the consumer continues using the registry normally. That is a useful allocation of responsibility because the consumer cannot repair a maintainer's authentication configuration. The registry can establish a minimum condition for publication that applies before a distribution reaches the people who depend on it.

Today's completion announcement reports more than 474,000 notification emails and recalls a hardware-key giveaway for eligible maintainers. Those details show that enforcement followed both communication and assistance. Making 2FA compulsory creates an account-access dependency for release maintainers, so supporting enrollment belongs in the implementation of the policy itself. The resulting publication model now combines an enrolled human account with a separate upload mechanism suited to automation.