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

Fixing npm EOTP One-Time Password Errors

EOTP means the npm registry wants a one-time password from an authenticator app before it will complete the request. It is the registry enforcing two-factor authentication — on your account, on the package, or on the organisation — for a write operation such as publishing, changing dist-tags, deprecating or managing access. Interactively, npm simply prompts for the code. In CI, where nobody can type it, the job fails. The fix depends on context: provide the code for manual operations, and for automation use trusted publishing or a token type the package's settings allow — never by weakening 2FA for people. This guide explains when EOTP appears and the right fix for each case.

Exact symptoms and error messages

In a CI log:

npm notice Publishing to https://registry.npmjs.org/ with tag latest and public access
npm error code EOTP
npm error This operation requires a one-time password from your authenticator.
npm error You can provide a one-time password by passing --otp=<code> to the command you ran.
npm error If you already provided a one-time password then it is likely that you either typoed
npm error it, or it timed out. Please try again.

Interactively, the same condition produces a prompt instead of an error:

$ npm publish
npm notice Publishing to https://registry.npmjs.org/ with tag latest and public access
This operation requires a one-time password.
Enter OTP:

Some operations use web-based authentication instead, printing a URL to open in the browser; the principle is the same.

Root cause analysis

Three settings can require a one-time password for writes. How publishing authentication fits together is covered in npm Registry Publishing Workflows.

Where a 2FA requirement for publishing can come from Account-level 2FA for writes, package-level publishing settings, and organisation-level 2FA requirements each can make the registry demand an OTP. 1 Account 2FA: auth-and-writes your npm account requires OTP for publishes and other writes 2 Package publishing setting require 2FA, or require 2FA and disallow tokens 3 Organisation policy members must have 2FA enabled 4 Token type in use decides whether the requirement can be satisfied without an OTP
Any one of these layers is enough to require an OTP for an interactive user's publish.

Whether a request can proceed without an OTP depends on how it is authenticated:

  • A session from npm login (a user identity) must provide an OTP when 2FA for writes applies.
  • A granular access token can be created with a setting to bypass 2FA for publishing, and works only if the package's publishing settings allow tokens.
  • Legacy "publish" tokens behave like the user's session and require an OTP; legacy "automation" tokens bypassed 2FA, but npm has been retiring classic tokens in favour of granular tokens and trusted publishing.
  • Trusted publishing (OIDC) does not involve a user session, so no OTP is required.

EOTP in CI therefore means the job authenticated as something that is subject to the OTP requirement: a token without bypass rights, a token type the package does not accept, or a copied ~/.npmrc from a developer session.

Resolution

Fixing EOTP by context Manual publishes supply an OTP, CI publishes use trusted publishing where possible, and otherwise a granular token allowed by the package settings. Where did EOTP happen? who or what was authenticated? Provide the OTP enter it, or --otp=123456 your terminal Trusted publishing OIDC; no token, no OTP CI, npmjs.com Granular token bypass 2FA + allowed by package CI, no OIDC
Keep 2FA for people; give automation an identity that does not need a human in the loop.

Manual operations: supply the code

npm publish --otp=123456
npm dist-tag add @acme/sdk@2.3.1 latest --otp=123456
npm deprecate @acme/sdk@"<2.0.0" "Upgrade to 2.x" --otp=123456

Codes expire after about thirty seconds; if a long prepublishOnly step runs first, npm may prompt at the end instead — or run the build first and publish separately.

CI on the public registry: use trusted publishing

Trusted publishing replaces tokens entirely and does not need an OTP. It also lets you set the package to "require 2FA and disallow tokens", which is the strongest configuration. Setup is covered in Publishing from CI with npm Trusted Publishing.

CI without OIDC: a granular token allowed by the package

Where trusted publishing is not available (self-hosted CI, some providers), create a granular access token that:

  • has read and write permission,
  • is limited to the packages or scope the job publishes,
  • has "bypass two-factor authentication" enabled for publishing,
  • has an expiry, and ideally allowed IP ranges for your CI egress.

Then check the package's publishing access setting: "Require two-factor authentication or a granular access token with bypass 2FA enabled" allows it; "Require two-factor authentication and disallow tokens" does not. Store the token as a masked CI secret and reference it only in the publish step:

- run: npm publish
  env:
    NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}

Do not fix EOTP by disabling 2FA on the account, downgrading the package setting to "don't require 2FA", or sharing a person's session in CI. Each of those widens the blast radius of a leaked credential — exactly what 2FA exists to limit.

Authentication methods and their OTP behaviour Compares npm login sessions, legacy publish tokens, granular tokens with bypass, and trusted publishing on whether an OTP is required and suitability for CI. OTP needed suitable for CI risk if leaked npm login session yes (2FA) no high legacy publish token yes no high granular token + bypass no yes scoped + expiring trusted publishing (OIDC) no best nothing to leak
Only identities designed for automation should publish from CI.

Diagnosing which credential the job used

When EOTP appears in CI, the first question is which identity the request carried. Three quick checks answer it without exposing the secret:

# Which account does the configured credential belong to?
npm whoami --registry https://registry.npmjs.org/

# Which .npmrc files are in play, and which one sets the auth token?
npm config ls -l | grep -E "userconfig|globalconfig|_authToken"

# List tokens on the account (run locally by a maintainer) and compare created dates and types
npm token list

If npm whoami prints a person's username rather than a dedicated automation account, the job is using a personal credential — the most common source of EOTP in CI and a risk on its own. If the token list shows the CI token as a classic publish token or a granular token without bypass enabled, recreate it with the right settings. If the workflow is supposed to use trusted publishing but npm whoami fails, the OIDC exchange is not happening: check the npm version and the id-token: write permission.

A useful habit is to publish from a dedicated automation user or, better, from trusted publishing only, so that any appearance of a human username in release logs is itself a signal that something is misconfigured.

Interactive publishing done safely

Some teams still publish certain packages by hand — rarely released internal tools, or emergency patches when CI is down. For those, keep the OTP requirement and make the process predictable: run the build and tests first, then publish in a separate command with --otp so the code does not expire during a long build; publish from a clean checkout of the release tag rather than a working directory with local changes; and use npm publish --dry-run immediately before the real command to confirm the contents and the target registry, as described in Dry-Running a Publish Before Release. Record manual publishes in the changelog or release notes, because they bypass the audit trail CI provides.

Worked example: a release that broke after an organisation policy change

An organisation turns on "require 2FA for publishing" for all its packages after a security review. The next automated release fails with EOTP: the CI job used a legacy token created years ago by a maintainer. The team configures trusted publishing for their public packages, and for two packages published from a self-hosted runner without OIDC, creates granular tokens with bypass enabled, scoped to those packages, expiring in 90 days, with the runner's egress IPs allowed. The legacy token is revoked the same day. Releases work again, and no credential capable of publishing is older than 90 days.

Other write operations

EOTP is not limited to npm publish. Any write that 2FA protects — npm dist-tag add, npm deprecate, npm access, npm owner add, npm unpublish — can trigger it. Automation that moves dist-tags after a release (for example, promoting a canary to latest) needs the same treatment as publishing: run it in the trusted-publishing job or with an appropriately scoped token. Operations that affect ownership and access are usually best left manual, with a person supplying the OTP, because they change who can publish at all.

Prevention and guardrails

  • Prefer trusted publishing for every package that can use it.
  • Use granular, expiring, package-scoped tokens where tokens are unavoidable.
  • Never copy personal .npmrc files into CI.
  • Review package publishing settings whenever you change how releases authenticate.

Frequently Asked Questions

Can I generate the OTP automatically in CI from a TOTP secret? Technically yes, but it puts the account's second factor next to its token, which defeats the purpose of 2FA. Use an automation identity instead.

Why did EOTP appear suddenly without any change on our side? Registry or organisation policy changes can add 2FA requirements, and npm has been phasing out classic token types. Check the package's publishing settings and the token's type.

Does EOTP happen on private registries? Only if the registry implements npm's OTP protocol. Most private registries (Verdaccio, Artifactory, GitHub Packages) use their own authentication and do not return EOTP.

Do security keys (WebAuthn) change anything? They change how you complete 2FA interactively — npm may open a browser for web-based authentication instead of prompting for a code — but not the automation story. CI still needs trusted publishing or an appropriately configured token.

Related

npm Registry Publishing Workflows