Publishing from CI with npm Trusted Publishing
Long-lived npm tokens stored as CI secrets are one of the most attacked parts of the JavaScript supply chain: a leaked token can publish a malicious version of your package from anywhere, until someone notices and revokes it. npm trusted publishing removes the token. Your CI provider proves the job's identity to npm with a short-lived OpenID Connect (OIDC) token, npm checks it against a trusted publisher you configured on the package, and the publish is authorised for that one job only. Provenance attestations are generated automatically. This guide configures trusted publishing for GitHub Actions and GitLab CI, covers monorepos and release tools, and explains the failure messages you may see. For most public packages it is now the recommended way to publish from CI.
How trusted publishing works
With a token, npm authorises a publish because the request carries a secret. With trusted publishing, npm authorises it because the CI provider vouches for exactly which repository, workflow and (optionally) environment is running. The general publishing flow is covered in npm Registry Publishing Workflows.
The registry checks the token's claims — repository owner and name, workflow file, and environment if you set one — against the trusted publisher configured for the package. A job in a fork, a different workflow file or another repository gets a token with different claims and is refused.
Requirements
- npm CLI 11.5.1 or later in the publishing job (Node.js 24 ships a compatible npm; on older Node.js, run
npm install -g npm@latestfirst). - A supported CI provider: GitHub Actions (GitHub-hosted runners) and GitLab CI/CD (GitLab.com shared runners) at the time of writing.
- The package must already exist on the registry to configure a trusted publisher in its settings. For a brand-new package, publish the first version with a token (or through an existing trusted setup for the scope), then switch.
- Workflow permission to request an OIDC token:
id-token: writein GitHub Actions.
Configuring the trusted publisher
On npmjs.com, open the package's Settings, find Trusted Publisher, choose GitHub Actions (or GitLab CI/CD), and enter:
- the organisation or user and repository name,
- the workflow filename (for example
release.yml), - optionally an environment name (for example
npm), which lets you add GitHub environment protection rules such as required reviewers.
Then restrict token access for the package: in the same settings, choose to require two-factor authentication and disallow tokens, so only the trusted publisher can publish. That is the step that actually removes the token attack path.
The workflow
# .github/workflows/release.yml — filename must match the trusted publisher config
name: release
on:
push:
tags: ["v*"]
permissions:
contents: read
id-token: write # required for the OIDC token
jobs:
publish:
runs-on: ubuntu-latest
environment: npm # optional; must match if configured on npm
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
registry-url: https://registry.npmjs.org
- run: npm ci
- run: npm run build
- run: npm test
- run: npm publish --access public
No NODE_AUTH_TOKEN is set. npm detects the OIDC environment, exchanges the identity token, and publishes. Provenance is attached automatically, so npm view your-lib --json shows an attestation and the package page shows where it was built — see Setting Up npm Provenance with GitHub Actions.
For GitLab CI, the job requests an ID token with the npm audience:
publish:
image: node:24
id_tokens:
NPM_ID_TOKEN:
aud: "npm:registry.npmjs.org"
script:
- npm ci && npm run build
- npm publish --access public
rules:
- if: $CI_COMMIT_TAG
Migrating from tokens step by step
Switching an existing package from token publishing to trusted publishing is low-risk if you keep the token as a fallback until the first trusted release succeeds.
Publishing a prerelease under a non-default dist-tag for the first trusted publish means a configuration mistake cannot affect latest users. Once it succeeds, check the attestation (npm view your-lib@2.3.0-rc.0 --json | jq .dist.attestations) and then promote normal releases through the same workflow.
What an attacker can and cannot do afterwards
It is worth being precise about the protection. With trusted publishing and token publishing disallowed, a stolen CI secret, a leaked .npmrc or a compromised laptop's token cannot publish the package. What remains is the workflow itself: anyone who can change the release workflow on the trusted branch, or trigger it with malicious code, can publish. That is why protected environments with required reviewers, branch protection on the release branch and code review for workflow changes matter as much as the OIDC setup. Trusted publishing moves the security boundary from "whoever holds the token" to "whoever can get code into the release workflow" — a much smaller and more auditable group, but still one to guard.
Monorepos and release tools
Trusted publishers are configured per package. In a monorepo that publishes twenty packages, each needs a trusted publisher entry pointing at the same repository and workflow. Do it once per package in the npm settings (scripts using the registry API can help for large numbers of packages), and keep the release workflow filename stable, because renaming it breaks every entry.
Release tools work as long as they call a new enough npm (or a package manager that delegates to it) inside the job that has id-token: write:
- Changesets —
changeset publishruns the package manager's publish; ensure npm 11.5.1+ is onPATHand pnpm or Yarn versions support OIDC delegation, or publish with npm directly. - semantic-release —
@semantic-release/npmneeds a version with OIDC support; removeNPM_TOKENonce trusted publishing works. - pnpm —
pnpm publishuses npm's authentication flow for the registry; check your pnpm version's release notes for OIDC support, and fall back tonpm publishin each package folder if needed (afterpnpm packhas rewrittenworkspace:ranges, publish the tarball).
Troubleshooting
npm error code E404
npm error 404 Not Found - PUT https://registry.npmjs.org/@acme%2fsdk - Not found
A 404 during a trusted publish usually means the OIDC exchange did not authorise this job — the workflow filename, repository or environment does not match the trusted publisher entry — so the request was treated as unauthenticated. Compare the entry with the workflow exactly, including case. The general E404 case is covered in Fixing npm publish E404 Not Found.
npm error need auth This command requires you to be logged in
The npm version is too old to use OIDC, or id-token: write is missing. Print npm --version in the job and check permissions.
Reusable workflows need care: the identity claims name the calling or called workflow depending on provider configuration, so configure the trusted publisher with the workflow file that actually runs npm publish, and test with a prerelease first.
Worked example: removing the last npm token
An organisation with eight public packages stores a single automation token in each repository's secrets. After a phishing incident at another company leads to malicious versions of popular packages, they move to trusted publishing: they upgrade the release workflows to Node.js 24, add id-token: write, configure trusted publishers for all eight packages with an npm environment that requires a maintainer's approval, publish a patch release of each to confirm it works, and then set every package to disallow token publishing and revoke the tokens. A leaked CI secret can no longer publish anything.
Prevention and guardrails
- Disallow token publishing on packages once trusted publishing works.
- Use a protected environment with required reviewers for the publish job.
- Keep the workflow filename stable and publish from one workflow only.
- Verify provenance after each release with
npm audit signaturesin a consumer project.
Frequently Asked Questions
Do I still need 2FA on my npm account? Yes. Trusted publishing secures CI publishing; your account still controls package settings and could publish manually. Keep 2FA on every maintainer account, as covered in Requiring 2FA for Package Maintainers.
Can I publish a brand-new package with trusted publishing? The trusted publisher is configured in the package's settings, so the package must exist first. Publish an initial version with a token (and then revoke it), or reserve the name with a placeholder release.
Does trusted publishing work with self-hosted runners? Support depends on the provider and npm's current policy; at launch, cloud-hosted runners were supported. Check npm's documentation before relying on self-hosted runners.
Does trusted publishing work for private packages on npmjs.com? Yes, for packages in paid organisations and accounts; the mechanism is the same. For private registries such as GitHub Packages or AWS CodeArtifact, each registry has its own OIDC or token model.
Related
- npm Registry Publishing Workflows covers the publishing process end to end.
- Setting Up npm Provenance with GitHub Actions explains the attestations trusted publishing produces.
- Rotating npm Publish Tokens Without Breaking CI handles registries where tokens remain necessary.
- Supply-Chain Security Hardening places trusted publishing among other supply-chain controls.