Back to publishing & release Automate semantic versioning Manage release channels Harden the supply chain

Requiring 2FA for Package Maintainers

Most npm supply-chain compromises do not break any cryptography; they take over a maintainer's account — a reused password, a phished login, a stolen session — and publish a malicious version. Two-factor authentication stops the password-only takeover, and phishing-resistant factors such as security keys stop most phishing too. Both are cheap compared with the cost of a malicious release reaching thousands of downstream projects. On npm you can require 2FA at three levels: each maintainer's account, each package's publishing settings, and the organisation. This guide sets up all three, explains how 2FA interacts with CI publishing, and covers the operational details — recovery codes, security keys and offboarding — that decide whether the policy holds up.

Why accounts are the weak point

A package is exactly as trustworthy as the least protected account that can publish it. Consider a package with five maintainers: if one has 2FA disabled and reuses a password found in a breach dump, an attacker can publish from that account, and every consumer installing the next version runs their code. The large 2025 compromises of widely used packages followed this pattern — a maintainer phished into entering credentials and a 2FA code on a convincing fake login page — which is why phishing-resistant factors matter as much as 2FA itself. The broader controls are covered in Supply-Chain Security Hardening.

Where 2FA can be required on npm Account-level 2FA for each maintainer, package-level publishing settings, organisation-level member requirements, and trusted publishing for CI. 1 Account: auth-and-writes each maintainer needs a second factor to log in and to publish or change settings 2 Package: require 2FA publishing needs 2FA, optionally disallowing tokens entirely 3 Organisation: require member 2FA members without 2FA cannot be part of the org 4 CI: trusted publishing OIDC identity instead of a token, so no 2FA exemption is needed
Require 2FA at every level, and move CI to trusted publishing so automation never needs an exemption.

Level 1: every maintainer's account

Each maintainer enables 2FA for authorisation and writes — the mode that protects both logins and publishes:

npm profile enable-2fa auth-and-writes
npm profile get                        # shows "two-factor auth: auth-and-writes"

Use a security key (WebAuthn) where possible; npm supports security keys and platform authenticators alongside authenticator apps. Security keys are bound to the real site's domain, so a phishing page on a look-alike domain cannot collect a usable second factor — the main weakness of one-time codes. Store the recovery codes offline; losing both factor and codes means going through npm support to regain the account. Register two security keys where possible — one carried daily and one kept somewhere safe — so a lost key never leaves a maintainer locked out in the middle of a release.

Level 2: each package's publishing settings

On the package's settings page, the Publishing access options are:

  • Don't require two-factor authentication — avoid.
  • Require two-factor authentication or a granular access token with bypass 2FA enabled — a person must use 2FA; automation can use an explicitly permitted token.
  • Require two-factor authentication and disallow tokens — only interactive 2FA publishes, or trusted publishing, which is not a token.

The CLI can set the MFA requirement too:

npm access set mfa=publish @acme/sdk      # require 2FA for publishing
npm access get status @acme/sdk

With trusted publishing configured for CI, the strictest option — 2FA required, tokens disallowed — becomes practical, because nothing legitimate needs a token. That setup is covered in Publishing from CI with npm Trusted Publishing.

Level 3: the organisation

For packages owned by an npm organisation, owners can require 2FA for all members in the organisation settings. Members without 2FA are prompted to enable it, and cannot perform organisation actions until they do. Combine it with team-based access so each package is writable only by the team responsible for it:

npm team create acme:sdk-maintainers
npm team add acme:sdk-maintainers alice
npm access grant read-write acme:sdk-maintainers @acme/sdk
npm access list collaborators @acme/sdk
Second-factor options for maintainers Compares SMS-free authenticator apps, security keys and platform passkeys on phishing resistance, recovery and convenience. phishing-resistant recovery convenience authenticator app (TOTP) codes can be relayed recovery codes type a code hardware security key domain-bound register two keys tap platform passkey domain-bound synced or second device biometric
Security keys and passkeys resist phishing; authenticator app codes can be relayed by a fake login page.

2FA and automation

2FA protects people; automation needs its own identity. Do not solve CI failures by relaxing 2FA:

  • Preferred: trusted publishing — no token, no OTP, and compatible with "disallow tokens".
  • Otherwise: a granular access token with "bypass 2FA" for publishing, limited to the packages it publishes, with an expiry, and permitted by the package's publishing setting.
  • Never: a maintainer's personal token or session in CI, or a TOTP secret stored next to a token to generate codes automatically.

When CI fails with EOTP, the credential it used is subject to 2FA — see Fixing npm EOTP One-Time Password Errors.

Rolling out a 2FA requirement

Turning on strict requirements overnight can lock out maintainers and break releases. A staged rollout avoids both.

A staged 2FA rollout for an organisation Inventory maintainers and publishing paths, move CI to trusted publishing, require member 2FA, then tighten package settings to disallow tokens. Week 1: inventory owners, teams, tokens, CI Week 2: CI identity trusted publishing / scoped tokens Week 3: members require org 2FA; keys issued Week 4: packages require 2FA, disallow tokens
Fix automation before enforcing strict package settings, so no release depends on an exemption.

Start with an inventory: for each package, list owners (npm owner ls), teams with access (npm access list collaborators), and every CI workflow or script that publishes or changes dist-tags. Move automation first, because once packages disallow tokens, any workflow still using one fails. Then announce and enable the organisation-level member requirement, giving maintainers a short window and hardware keys where the budget allows. Finally, tighten each package's publishing setting. Communicate each step; the most common rollout failure is a maintainer discovering the new requirement in the middle of an urgent release.

Protecting the rest of the publishing path

Account 2FA protects the registry side; the source side matters just as much, because trusted publishing and CI tokens publish whatever the release workflow builds. Require 2FA on your source-control organisation as well, protect the release branch and tags, require review for changes to release workflows, and restrict who can approve deployments to the publishing environment. An attacker who cannot log in to npm but can push to the release branch still gets their code published. Treat the combination — registry account, source-control account, CI configuration and publishing environment — as one system, and apply the same second-factor and review requirements across all of it.

Maintainer hygiene and offboarding

2FA is necessary but not sufficient. The account list itself needs care:

  1. Review maintainers regularly. npm owner ls @acme/sdk lists everyone who can publish. Remove people who no longer maintain the package with npm owner rm <user> @acme/sdk — dormant accounts are attractive targets.
  2. Prefer organisation teams over individual owners so access follows team membership, and offboarding is one removal.
  3. Keep at least two active maintainers with 2FA per package so a lost device does not strand the package.
  4. Watch for publishing notifications. npm emails maintainers on each publish; an unexpected email is often the first sign of a compromise.

Worked example: raising the bar across forty packages

An organisation maintains forty public packages with a mix of individual owners, some without 2FA. They enable "require 2FA for members" at the organisation level, move package ownership from individuals to four teams, set every package to "require 2FA and disallow tokens", and configure trusted publishing for the CI workflows that publish them. Maintainers register hardware security keys, and a quarterly script lists owners and teams for every package and flags any individual owner outside a team. The only way to publish is now a reviewed CI workflow or a maintainer with a physical key.

Prevention and guardrails

  • Require auth-and-writes 2FA for every maintainer, with security keys where possible.
  • Set packages to require 2FA, and disallow tokens once CI uses trusted publishing.
  • Manage access through organisation teams and review owners quarterly.
  • Never exempt people to make automation work; give automation its own identity.

Frequently Asked Questions

Does 2FA stop every account takeover? It stops password-only attacks. One-time codes can still be phished in real time; security keys and passkeys stop that. Session theft from a compromised machine is a separate risk addressed by device security.

What happens if a maintainer loses their device? They use a recovery code, or another maintainer with access keeps the package publishable while they recover. That is why every package should have at least two maintainers with 2FA.

Does requiring 2FA affect consumers? No. Installing packages does not require authentication for public packages; 2FA only affects writes by maintainers.

Can a package require 2FA if one maintainer refuses to enable it? Yes. The package setting applies to every publish; a maintainer without 2FA simply cannot publish or change settings until they enable it. For organisation packages, the member requirement also blocks them from organisation actions.

Should maintainers use separate accounts for publishing? It is not required, but some teams keep a dedicated, key-protected account for the rare manual operations (ownership changes, emergency deprecations) and use trusted publishing for everything else. Whatever you choose, never share an account between people.

How do I know if a package was published without 2FA? npm does not expose per-publish authentication details publicly, but provenance attestations show which CI workflow built a version. Requiring provenance for every release makes unexpected manual publishes stand out.

Related

Supply-Chain Security Hardening