How to Publish npm Packages with GitHub Actions Trusted Publishing

secure npm publishing gihub actions trusted publishing

You can publish an npm package from GitHub Actions without storing a long-lived npm publish token in repository secrets. npm's trusted publishing feature uses OpenID Connect (OIDC) to let a specifically configured workflow request short-lived publishing credentials.

When the workflow publishes a public package from a public GitHub repository, npm can also attach a provenance attestation describing where and how the package was built. Trusted publishing and provenance are related, but they solve different problems: OIDC replaces a reusable credential, while provenance gives consumers verifiable build-origin information. Neither one proves that the package's code is safe.

This guide configures a GitHub Actions release workflow, explains the requirements and common failure points, and shows how to check the resulting attestations. It follows npm's trusted publisher documentation and provenance guidance. For the underlying GitHub identity exchange, see our guide to GitHub Actions OIDC for secure cloud deployments.

Check whether your package is ready

npm's current trusted publishing requirements include:

  • A supported runner. GitHub Actions must use a GitHub-hosted runner. Self-hosted runners are not currently supported for npm trusted publishing.
  • A recent toolchain. Use Node.js 22.14.0 or later and npm CLI 11.5.1 or later. The example below uses Node.js 24 and updates npm to a compatible 11.x release.
  • A matching repository URL. The repository.url value in your package's package.json must exactly match the GitHub repository that runs the workflow.
  • A public source and package for provenance. npm's automatic provenance generation requires both a public repository and a public npm package. A private repository can use trusted publishing, but npm will not generate provenance for it.

Trusted publishing can authenticate the publish step; it does not authenticate npm ci. If your build installs private dependencies, use a separate read-only npm token for that install. Store it in GitHub Actions secrets, expose it only to the install step, and do not reuse it as a publish credential. Our guide to environment variables and secrets in web projects covers safe handling of workflow secrets.

Configure the trusted publisher on npm

Open the package's settings on npmjs.com and add a trusted publisher under Publishing access. Choose GitHub Actions, then enter the GitHub account or organization, repository, and workflow filename. Enter only the filename, such as publish.yml, not the .github/workflows/ directory. The workflow file itself must be in that directory. If you use a GitHub deployment environment, configure its exact name as well.

These values are case-sensitive, and npm does not verify them when you save the settings. Check them against the repository and workflow before attempting a release. After creating the configuration, complete its first successful publish within two days; npm uses that publish to validate and bind the configuration to the repository.

Add a release workflow

Save the following as .github/workflows/publish.yml and configure publish.yml as the trusted publisher's workflow filename:

name: Publish to npm

on:
  push:
    tags:
      - 'v*'

permissions:
  contents: read

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: '24'
          registry-url: 'https://registry.npmjs.org'
          package-manager-cache: false
      - name: Update npm CLI
        run: npm install --global 'npm@^11.5.1'
      - run: npm ci
      - run: npm run build --if-present
      - run: npm test --if-present
      - run: npm publish --access public

The job-level id-token: write permission lets the workflow request an OIDC token; it does not give the job permission to change unrelated GitHub resources. npm checks the token against the trusted publisher you configured. Keep this permission on the publishing job rather than granting it to every job in the workflow.

The tag trigger keeps publishing separate from ordinary pull-request checks. Protect release tags so that only an authorized maintainer can create them. If you add a GitHub environment for approval or deployment rules, set the same environment name in the npm trusted publisher settings. GitHub Actions' major-version tags make examples readable; for stronger supply-chain controls, review actions and pin them to verified full commit SHAs.

The --access public flag is useful when a scoped package is first published publicly. Omit it if your package is intentionally private; private packages do not receive provenance attestations. The release job does not need an NPM_TOKEN for publishing.

Verify the release and its provenance

Once the tag-triggered workflow succeeds, check the published package's npm page for its provenance details. You can also inspect attestations for dependencies installed in a project by running:

npm audit signatures

This command checks registry signatures and provenance attestations for installed packages. Use a current npm CLI so it can verify the current attestation format. A verified attestation helps establish a package's origin and build workflow; it is not a code review, vulnerability scan, or guarantee that the source or build was trustworthy.

Migrate carefully from an npm token

If you already publish with an NPM_TOKEN, add and test the trusted publisher before removing the existing credential. If your package has private dependencies, keep a read-only token for dependency installation, but scope it only to npm ci; the publish step should use OIDC.

After all legitimate publishing workflows have moved and you have confirmed that releases succeed, remove unused write tokens. npm also recommends enabling Require two-factor authentication and disallow tokens in the package's publishing-access settings when that fits your release process. Do not disable token access until any required manual or legacy publishing path has been replaced.

If a release fails with an authentication error, check these items first:

  1. The npm trusted publisher's account, repository, and workflow filename match exactly. The configured filename is publish.yml, not .github/workflows/publish.yml.
  2. The publishing job has id-token: write and runs on a GitHub-hosted runner.
  3. The package's repository.url exactly matches the GitHub repository.
  4. The Node.js and npm CLI versions meet npm's current requirements.
  5. If you use a GitHub environment, its name matches the value configured for the trusted publisher.

For a temporary migration path, GitHub Actions can publish with a token and request provenance separately using npm publish --provenance. That still requires a publishing token; it is not a substitute for trusted publishing. npm's provenance instructions list the additional runner, permissions, and public-package requirements.

Official references

Leave a Reply