ci: npm Trusted Publishing (OIDC), drop NPM_TOKEN - #386
Conversation
Switch npm publishing from the long-lived NPM_TOKEN secret to npm Trusted
Publishing over GitHub Actions OIDC.
- Upgrade npm to 11.6.2 after setup-node (OIDC needs npm >= 11.5.1).
- Drop env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} from the publish step(s).
Requires a one-time trusted-publisher registration per package on npmjs.com
(repo dodopayments/billingsdk + workflow release.yml). Mirrors dodopayments/dualmark#91.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Hi! I'm the It looks like you correctly set up a CI job that uses the autofix.ci GitHub Action, but the autofix.ci GitHub App has not been installed for this repository. This means that autofix.ci unfortunately does not have the permissions to fix this pull request. If you are the repository owner, please install the app and then restart the CI workflow! 😃 |
There was a problem hiding this comment.
Good direction — moving off a long-lived NPM_TOKEN to OIDC trusted publishing is the right call, and the permissions block already had id-token: write, so the diff is appropriately minimal. I traced the npm 11.6.2 OIDC implementation and ran the upgrade step to check it, and found one blocking issue plus a few notes.
Blocking: npm@11.6.2 cannot be installed on Node 18 — this breaks the Release workflow
.github/workflows/release.yml:21 still pins node-version: '18', but npm@11.6.2 declares:
engines.node: ^20.17.0 || >=22.9.0
npm enforces engines strictly when npm itself is the install target, so this is a hard error rather than the usual EBADENGINE warning. I verified on Node 18.20.8:
npm install -g npm@11.6.2exits 1 withEBADENGINE/notsup, and npm remains at 10.8.2.- The identical command on Node 22 exits 0 and yields npm 11.6.2.
Since a non-zero run: step fails the job, the release job aborts at the new step and never reaches npm publish. And because NODE_AUTH_TOKEN is removed in the same commit, there's no fallback path — the net effect is that releases stop working entirely.
Suggested fix: bump the runtime to Node 22 (or at minimum 20):
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'Node 18 reached EOL on 2025-04-30, and ci.yml already runs Node 20, so this also removes an inconsistency between the two workflows. Keeping the pinned npm install -g npm@11.6.2 afterward is still reasonable for reproducibility.
Non-blocking notes
1. Add verbose logging to the publish step for the first OIDC release. npm's OIDC helper is explicitly written to never throw — every failure path (missing ACTIONS_ID_TOKEN_REQUEST_*, failed token exchange, no trusted publisher registered) just returns undefined and lets the publish continue unauthenticated. The result is an opaque auth error from the registry rather than "trusted publishing is not configured". All the useful diagnostics are logged at verbose/silly. Given the npmjs.com-side registration is being done out-of-band, I'd add:
run: npm publish --access public --provenance --loglevel verboseThat makes the oidc log lines visible if the first release fails.
2. Keeping registry-url in setup-node is correct — please don't drop it later. It writes a temp user-level .npmrc containing //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}. With that env var now unset this is harmless: modern @npmcli/config leaves the unresolved literal in place instead of throwing, and npm's OIDC flow overwrites the _authToken key at user scope after a successful exchange. The setting is still needed so npm resolves the correct registry (and therefore the correct OIDC audience). Worth knowing so it isn't "cleaned up" as dead config in a follow-up.
3. --provenance is now technically redundant but worth keeping. On a successful exchange npm auto-enables provenance for public repos/packages. Passing it explicitly turns provenance into a hard requirement rather than best-effort, which is a useful guard against silently publishing unsigned if the OIDC path no-ops. I'd leave it as-is.
4. Revoke the NPM_TOKEN secret once this lands. The security benefit described in the PR isn't actually realized until the repo/org secret and the underlying npm automation token are deleted. NODE_AUTH_TOKEN is gone from the workflow after this change, so nothing else references it.
5. Formatting nit: there's a double blank line before the new step and no blank line between it and Set version from release tag, which is inconsistent with the spacing used elsewhere in the file.
Scope check
Only @billingsdk/cli (packages/cli) is published by this workflow; the root package is private: true and nothing under packages/templates is published. So the trusted-publisher registration described in the PR body is a single package, and the --file release.yml value matches this workflow's filename.
Happy to re-approve once the Node version is bumped.
| # (11.5.2 fixed an OIDC/provenance ordering bug, 11.6.2 resolves | ||
| # trusted-publishing failures seen in the wild). | ||
| - name: Upgrade npm for trusted publishing | ||
| run: npm install -g npm@11.6.2 |
There was a problem hiding this comment.
This step fails on Node 18, which is what node-version: '18' pins above.
npm@11.6.2 declares engines.node: ^20.17.0 || >=22.9.0, and npm enforces engines strictly when npm itself is the install target — so this is a hard EBADENGINE/notsup error, not the usual warning. Verified on Node 18.20.8: the command exits 1 and npm stays at 10.8.2. The same command on Node 22 exits 0 and produces npm 11.6.2.
Because the job stops on the non-zero exit, npm publish is never reached — and with NODE_AUTH_TOKEN removed in this same commit there's no fallback, so releases break outright.
Bumping node-version to '22' (or '20') resolves it and also aligns with ci.yml, which already runs Node 20. Node 18 has been EOL since 2025-04-30.
What
Switch npm publishing from a long-lived
NPM_TOKENsecret to npm Trusted Publishing over GitHub Actions OIDC — no token in CI.Changes
11.6.2aftersetup-node(OIDC trusted publishing requires npm >= 11.5.1; the Node version here ships an older npm). Pinned for reproducibility — 11.5.2 fixed an OIDC/provenance ordering bug and 11.6.2 resolves trusted-publishing failures seen in the wild.env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}from the publish step(s).Required before the next release (npmjs.com side — cannot be done from code)
Each published package must have a trusted publisher registered on npmjs.com pointing at this repo + the workflow file
.github/workflows/release.yml, or the first OIDC publish fails auth. This is being handled out-of-band per package (npm trust github ... --repository dodopayments/billingsdk --file release.yml --allow-publish). A brand-new package needs a manual first publish before OIDC can take over.Why
dodopayments/dualmark(Feat: Added google analytics for tracking #91).