· via dev.to (home feed)
npm publish 404s can hide missing OIDC trusted publishing support in CI
As npm sunsets 2FA-bypass granular tokens, CI workflows migrating to OIDC trusted publishing can fail with misleading 404s when the bundled npm version predates 11.5.1.

npm is retiring 2FA-bypass granular tokens
According to a developer writeup on dev.to, npm's granular access tokens are losing the ability to bypass two-factor authentication in stages. Since 31 July 2026 these tokens can no longer perform account, organisation or package management operations, which includes editing trusted publishing configuration. In January 2027 they also lose direct publish rights, leaving them able only to read private packages and stage a release that a maintainer must then approve with 2FA.
The intended replacement is OIDC Trusted Publishing: instead of storing a long-lived token as a CI secret, a maintainer registers a GitHub repository and workflow on npmjs.com, and the workflow exchanges a short-lived OIDC token for registry access.
A 404 that lies
The post's author migrated one package and watched the release workflow fail four times in a row with an error claiming the package was not in the registry — despite the package existing, having published versions and belonging to the same account.
The writeup identifies the root cause: the workflow pinned Node 20, which bundles npm 10.8.2, while OIDC Trusted Publishing only landed in npm 11.5.1. Support in npm 10 isn't broken; it is entirely absent. The client never attempts the token exchange, falls back to whatever token sits in the generated .npmrc, and effectively publishes unauthenticated.
The reason this surfaces as a 404 rather than a 403 is deliberate registry behaviour, the post explains. Returning a 403 for a package you cannot write to would confirm that the package exists, leaking the existence of private packages to anyone who can guess a name. So npm answers 404 both for packages that do not exist and for packages you may not touch. On a package you own, that message should be read as an authentication problem rather than a naming one.
Two red herrings
The writeup details two misleading signals that cost debugging time.
First, provenance signing succeeded. The workflow signed a provenance statement and posted it to the sigstore transparency log, which looks like proof the OIDC token works. But provenance signing uses the OIDC id-token to sign an attestation, while Trusted Publishing exchanges the same id-token for a registry auth token — separate mechanisms on separate code paths, introduced in different npm versions. One can work perfectly while the other does not exist.
Second, the step environment showed NODE_AUTH_TOKEN as XXXXX-XXXXX-XXXXX-XXXXX, which resembles a masked secret. It is not: the post cites the source of actions/setup-node v4, which exports that literal dummy value when no token was supplied, purely so npm does not warn about a missing variable. The action's main branch has since changed to export the variable only when the user provides one, so the effect is version-specific. A pre-set token would not block OIDC anyway, since npm's publish command runs its OIDC helper before reading credentials and overwrites the auth token directly.
The fix
Two changes resolved it. The workflow moved to Node 24, which bundles npm 11.19.0, and added an explicit global install of npm 11.5.1 or newer as a guard. The guard matters because Node 24.0.0 shipped npm 11.3.0, below the threshold, so pinning the major alone is no guarantee. Printing npm --version into the log lets the next person rule the version out at a glance.
With authentication working, the failure changed to a 422: sigstore provenance only supports public source repositories, and the publishing repo was private. The correct fix was to drop the --provenance flag entirely rather than pass --no-provenance, because npm auto-enables provenance only when the flag is left at its default and the repository visibility is public.
Why it matters
Anyone publishing npm packages from CI faces this combination soon. The January 2027 cutoff removes direct publish from granular tokens and pushes maintainers toward OIDC Trusted Publishing, and workflows pinned to Node 20 or an older npm will fail in ways that point away from the real problem. The 404 reads as a registry or naming issue, the dummy token looks like a leaked credential, and a successful provenance step falsely clears OIDC. Checking npm --version — and requiring at least 11.5.1 — is the cheapest defence before the migration window closes.
- #npm
- #oidc
- #ci-cd
- #github-actions
- #package-management