Skip to content

registry-url writes _authToken line that breaks npm Trusted Publisher OIDC when no NODE_AUTH_TOKEN is set #1551

@rafael145a

Description

@rafael145a

Summary

When registry-url is set, actions/setup-node always writes //<host>/:_authToken=${NODE_AUTH_TOKEN} to .npmrc. This is correct for the classic token-auth flow, but silently breaks the npm Trusted Publisher (OIDC) flow when no NODE_AUTH_TOKEN secret is configured (the intended state for OIDC).

With Trusted Publisher set up for the package and id-token: write granted, npm publish reads the .npmrc, sees _authToken= (empty after placeholder expansion), assumes "auth is configured", and never initiates the OIDC token exchange. It then fails with ENEEDAUTH or E404.

This is the documented and recommended way to publish via Trusted Publishers (npm docs example) — yet the workflow doesn't work out of the box.

Reproduction

name: Publish (broken)
on: workflow_dispatch
permissions:
  id-token: write
  contents: read
jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '24'
          registry-url: 'https://registry.npmjs.org'
      - run: npm install -g npm@latest --force  # ensure 11.5.1+
      - run: npm publish --access public --provenance
        # Fails with `npm error code ENEEDAUTH` even though:
        # - Trusted Publisher is configured for the package on npmjs.com
        # - id-token: write is granted
        # - ACTIONS_ID_TOKEN_REQUEST_URL / TOKEN are set
        # - npm 11.5.1+ is installed

Verified with npm@11.14.1 against @sofereditor/core@0.2.0 (PUT https://registry.npmjs.org/@sofereditor%2fcoreENEEDAUTH).

Current workaround

Strip the line that the action wrote, after setup-node runs:

- run: |
    npmrc="${NPM_CONFIG_USERCONFIG:-$HOME/.npmrc}"
    sed -i '/_authToken/d' "$npmrc"

After that, npm publish detects ACTIONS_ID_TOKEN_REQUEST_* env vars and goes through the OIDC token exchange at /-/npm/v1/oidc/token/exchange/package/<pkg>. Same workflow then succeeds.

Proposed fix(es)

A few non-mutually-exclusive options, in increasing order of intervention:

  1. New input auth-token-line: true|false (or write-auth-token-line) — when false, write only the registry= line. Default true to preserve back-compat. Lets users opt out cleanly:

    - uses: actions/setup-node@v5
      with:
        registry-url: 'https://registry.npmjs.org'
        auth-token-line: false  # for Trusted Publisher OIDC
  2. Auto-detect OIDC environment: if id-token permission is granted (ACTIONS_ID_TOKEN_REQUEST_TOKEN exists) AND no NPM_TOKEN/NODE_AUTH_TOKEN secret is wired in the calling step's env, skip the _authToken line by default. This needs a heuristic — perhaps "skip if ACTIONS_ID_TOKEN_REQUEST_TOKEN is set and NODE_AUTH_TOKEN is empty at action-run time". Risk: false positives if user wants classic auth and just hasn't passed the env yet.

  3. Documentation update in the action README mentioning the interaction with Trusted Publishers, with a copy-pasteable workaround.

I'd be happy to PR (1) — minimal change, fully back-compat. Let me know which direction you prefer.

Cross-reference

Filed parallel issue at npm/documentation#1960 suggesting docs updates on the npm side.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions