Back to publishing & release Automate semantic versioning Manage release channels Harden the supply chain

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.

A trusted publish from GitHub Actions The workflow requests an OIDC token from GitHub, npm CLI exchanges it with the registry, the registry checks the trusted publisher configuration, and the publish proceeds with a provenance attestation. GitHub Actions job GitHub OIDC npm CLI npm registry request id-token (audience npm) signed JWT: repo, workflow, ref publish with OIDC token matches trusted publisher: accepted
No stored secret is involved; the identity token is minted for this job and expires within minutes.

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@latest first).
  • 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: write in 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
Token-based publishing versus trusted publishing Compares long-lived automation tokens with OIDC trusted publishing on secret storage, blast radius of a leak, rotation, provenance and fork safety. automation token trusted publishing Secret stored in CI yes, long-lived none If leaked publish from anywhere nothing to leak Rotation manual, periodic not needed Provenance with --provenance automatic Restricted to one workflow no yes (+ environment)
Trusted publishing removes the long-lived secret entirely and ties each publish to one workflow.

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.

Migrating a package from tokens to trusted publishing Configure the trusted publisher, update the workflow permissions and npm version, publish a prerelease without the token, then disallow tokens and revoke them. configure trusted publisher repo, workflow file, environment per package in npm settings update workflow id-token: write, npm 11.5.1+ publish a prerelease remove NODE_AUTH_TOKEN for this run e.g. 2.3.0-rc.0 on the next tag disallow tokens package setting: require 2FA, no tokens revoke old tokens and delete CI secrets
Keep the token until one trusted publish succeeds, then remove it everywhere in one step.

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:

  • Changesetschangeset publish runs the package manager's publish; ensure npm 11.5.1+ is on PATH and pnpm or Yarn versions support OIDC delegation, or publish with npm directly.
  • semantic-release@semantic-release/npm needs a version with OIDC support; remove NPM_TOKEN once trusted publishing works.
  • pnpmpnpm publish uses npm's authentication flow for the registry; check your pnpm version's release notes for OIDC support, and fall back to npm publish in each package folder if needed (after pnpm pack has rewritten workspace: 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 signatures in 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