$ SOLVED // it worked
Solved cross-platform npm registry account moderate

npm publish fails because 2FA is a passkey and there's no OTP to enter

Symptom npm publish wants a one-time password, but your 2FA is a passkey (Touch ID / security key) with no authenticator app, so there's no code to type; publish fails or 401s.

Updated 2026-06-21 Verified fix

Publishing to npm stalled because the account’s 2FA is a WebAuthn passkey (Touch ID / security key), with no TOTP authenticator app. Interactive npm publish expects to prompt for a 6-digit OTP, but there’s no code to enter — a passkey can’t produce one — so the publish either hangs waiting for input or fails with a 401.

The fix

Use a Classic → Automation token, which is designed for CI/non-interactive publishing and bypasses the 2FA prompt:

  1. npmjs.com → Access TokensGenerate New TokenClassic TokenAutomation.
  2. Copy the token and put it in ~/.npmrc:
//registry.npmjs.org/:_authToken=npm_xxxxxxxxxxxxxxxxxxxx
  1. npm publish now works with no OTP prompt.

If it’s a scoped org package

Two extra gotchas that produce a confusing 402/404-on-PUT:

  • Scoped packages (@org/name) default to private, which fails on a free org. Make it public:
{
  "publishConfig": { "access": "public" }
}
npm publish --access public
  • A GitHub org is not an npm org. Publishing an unscoped name pushes it under your personal account, not the org. The package name must be scoped @org/name to land in the npm org.

Don’t get burned later

  • npm tokens expire after ~90 days. If a publish that used to work suddenly 401s, or PUT returns 404, the token expired — mint a fresh Automation token and update ~/.npmrc.
  • Use a "files" allowlist in package.json, not just .npmignore. Without it, npm packs the entire working directory — it’s easy to accidentally ship tens of MB of local junk into the tarball.