Skip to content

A credential in .npmrc is what stops OIDC — and setup-node writes one - #5

Merged
softov merged 1 commit into
mainfrom
fix/oidc-authtoken-line
Aug 24, 2026
Merged

A credential in .npmrc is what stops OIDC — and setup-node writes one#5
softov merged 1 commit into
mainfrom
fix/oidc-authtoken-line

Conversation

@softov

@softov softov commented Aug 24, 2026

Copy link
Copy Markdown
Owner

The OIDC rehearsal passed, but its log showed this in the publish step's environment:

NODE_AUTH_TOKEN: XXXXX-XXXXX-XXXXX-XXXXX

That is actions/setup-node's placeholder, in a workflow that has no token and wants none. It would have broken the next release.

What happens

Given a registry-url, setup-node always writes _authToken=${NODE_AUTH_TOKEN} into an .npmrc. With no token to substitute, the line does not disappear — it becomes an empty credential rather than no credential. npm reads any _authToken line as "auth is configured", so it never performs the OIDC exchange, and fails with ENEEDAUTH or a 404.

That error reads as a permissions problem and is not one — it is a configuration problem wearing a permissions problem's message. Documented at actions/setup-node#1551 and npm/documentation#1960.

Trusted publishing works by npm noticing it has no credentials. Handing it an empty one is worse than handing it none.

The fix

registry-url is removed — npmjs.org is the default registry regardless, so nothing is lost.

scripts/check-no-npm-auth.mjs then runs before the publish and fails on a credential in any .npmrc npm would read (NPM_CONFIG_USERCONFIG, ~/.npmrc, the repo's own), or on NODE_AUTH_TOKEN being set at all. Verified against all four states:

State Result
No credential anywhere (CI) passes
setup-node's placeholder in .npmrc fails, names the file and line
NODE_AUTH_TOKEN set, no file fails, names the variable
Real tokens in ~/.npmrc (this machine) fails, as it should

The point is that the next reintroduction fails loudly in CI rather than at the registry, halfway through a release — which is how the last three attempts went.

Why this could not be caught by rehearsing

workflow_dispatch --dry-run runs the whole gate and publishes nothing, so it never authenticates. The first real exercise of OIDC auth is a real publish. That is exactly why this needed catching by reading, not by running.

RELEASING.md gains a section on the trap, and the changelog records it.

… one

`registry-url` makes actions/setup-node write `_authToken=${NODE_AUTH_TOKEN}`
into an .npmrc. With no token to substitute, the line does not disappear - it
becomes an empty credential, and npm reads any `_authToken` line as auth being
configured. So it never performs the OIDC exchange, and fails as ENEEDAUTH or a
404: a configuration problem wearing a permissions problem's error message
(actions/setup-node#1551).

The rehearsal is what showed it - `NODE_AUTH_TOKEN: XXXXX-XXXXX-XXXXX-XXXXX` in
the environment of a workflow that has no token and wants none.

`registry-url` is gone; npmjs.org is the default anyway. check-no-npm-auth.mjs
runs before the publish and fails on a credential in any .npmrc npm would read,
or on NODE_AUTH_TOKEN being set at all, so the next reintroduction fails loudly
instead of at the registry.
@softov
softov merged commit e31475a into main Aug 24, 2026
1 check passed
@softov
softov deleted the fix/oidc-authtoken-line branch August 24, 2026 21:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant