Files
changesets-action/docs/set-up-npm-auth.md
T
Bjorn Lu 469993ce57 Remove npm auth handling in favor of docs (#695)
* Add docs for npm auth setup

* Show bash examples

* Improve bash pattern

* Update and reference docs

* Remove setup auth code

* Update

* Update

* Update
2026-07-24 15:24:09 +00:00

7.7 KiB

How to set up npm authentication

This is a brief guide on setting up npm authentication in GitHub Actions. Most of the information below are also applicable outside of Changesets and can be referenced for other npm workflows.

It is recommended by npm to use Trusted Publishing, or Staged Publishing, or both, to securely publish packages from CI.

Note that Staged Publishing does not work with Changesets at the moment, so it's recommended to use Trusted Publishing instead for now. Check out its docs for more information to set it up.

Also, in contrary to npm's workflow recommendation, make sure the id-token: write is only set on the job that needs to publish. As such, consider splitting the build, test, publish flows etc into separate jobs. Here's an example setup with Changesets:

# .github/workflows/publish.yml
name: Publish

on:
  push:
    branches:
      - main

permissions: {} # recommended: reset permissions

jobs:
  select-mode:
    runs-on: ubuntu-latest
    outputs:
      mode: ${{ steps.select-mode.outputs.mode }}
      publish-plan-artifact-id: ${{ steps.select-mode.outputs.publish-plan-artifact-id }}
    permissions:
      contents: read # to check out repo (actions/checkout)
    steps:
      - uses: actions/checkout@v7
      - run: npm install
      - uses: changesets/action/select-mode@v2
        id: select-mode

  version:
    if: needs.select-mode.outputs.mode == 'version'
    needs: select-mode
    runs-on: ubuntu-latest
    outputs:
      version-dir-artifact-id: ${{ steps.version.outputs.version-dir-artifact-id }}
    permissions:
      contents: read # to check out repo (actions/checkout)
    steps:
      - uses: actions/checkout@v7
      - run: npm install
      - uses: changesets/action/version@v2
        id: version

  pack:
    if: needs.select-mode.outputs.mode == 'publish'
    needs: select-mode
    runs-on: ubuntu-latest
    outputs:
      pack-dir-artifact-id: ${{ steps.pack.outputs.pack-dir-artifact-id }}
    permissions:
      contents: read # to check out repo (actions/checkout)
    steps:
      - uses: actions/checkout@v7
      - run: npm install
      - run: npm build
      - uses: changesets/action/pack@v2
        id: pack
        with:
          publish-plan-artifact-id: ${{ needs.select-mode.outputs.publish-plan-artifact-id }}

  publish:
    needs: pack
    runs-on: ubuntu-latest
    permissions:
      id-token: write # for trusted publishing (changesets/action)
    steps:
      - uses: changesets/action/publish@v2
        with:
          pack-dir-artifact-id: ${{ needs.pack.outputs.pack-dir-artifact-id }}

Token-based Publishing

Caution

Token-based publishing (with Granular Access Tokens) is no longer recommended, with many restrictions that make it difficult to use in CI workflows. For example:

  • They expire after a maximum of 90 days, which requires periodic manual token rotation.
  • 2FA-bypass tokens are being deprecated and will soon be not allowed to publish packages with 2FA enabled.

However, if you're using a different npm-compatible registry that does not support Trusted Publishing or Staged Publishing, you may still opt for token-based publishing. Check out the next section for more information.

You'll need an npm token with "Bypass two-factor authentication" checked (to prevent npm requesting 2FA in CI). Add this token as a secret in your GitHub repo with the name NPM_TOKEN so it can be used in the workflow below.

In most cases, you can use actions/setup-node to set up token-based authentication automatically.

# .github/workflows/publish.yml
name: Publish

on:
  push:
    branches:
      - main

permissions: {} # recommended: reset permissions

jobs:
  # ... other jobs like select-mode and version
  publish:
    if: needs.select-mode.outputs.mode == 'publish'
    needs: select-mode
    runs-on: ubuntu-latest
    permissions:
      contents: read # to check out repo (actions/checkout)
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
          registry-url: https://registry.npmjs.org/ # set this option to set up npm authentication
      - run: npm install
      - run: npm build
      - uses: changesets/action/publish@v2
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} # pass the token here

Tip

Pass registry-url: https://npm.pkg.github.com/ and NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} to publish to the GitHub Package Registry instead of npm.

Internally, actions/setup-node will set up a .npmrc file that looks like this:

//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}

This syntax allows to authenticate with the npm registry only when the NODE_AUTH_TOKEN environment variable is set, which is the safer approach than storing the token directly in the .npmrc file.

Manual Setup

For advanced use cases, you can also set up the ~/.npmrc file manually. Make sure to remove the registry-url option from actions/setup-node to prevent conflicts with your custom .npmrc file.

For example, if you need to publish different scopes to different registries, you can set up the ~/.npmrc file like below:

- run: |
    cat << 'EOF' > ~/.npmrc

    # For unscoped packages, publish to the default npm registry
    //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}

    # For @foo/* packages, publish to a custom registry
    @foo:registry=https://my-registry.com/
    //my-registry.com/:_authToken=${NODE_FOO_AUTH_TOKEN}

    # For @bar/* packages, publish to the GitHub Package Registry
    @bar:registry=https://npm.pkg.github.com/
    //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}

    EOF

Package Managers Edge Cases

pnpm

An .npmrc file in a project directory with pnpm does not support environment variables due to security reasons. As such, it's recommended to set up in the home directory instead. This is also the general recommendation for other package managers to not mix potential existing config setups in projects.

yarn

Yarn does not support the .npmrc file, compared to every other package managers that do. To set up authentication for yarn, use a ~/.yarnrc.yml file instead:

- run: |
    cat << 'EOF' > ~/.yarnrc.yml
    npmAuthToken: "${NODE_AUTH_TOKEN}"
    EOF

For advanced use cases, similar to the .npmrc example above, the equivalent looks something like this:

- run: |
    cat << 'EOF' > ~/.yarnrc.yml

    npmAuthToken: "${NODE_AUTH_TOKEN}"

    npmScopes:
      foo:
        npmRegistryServer: "https://my-registry.com/"
        npmAuthToken: "${NODE_FOO_AUTH_TOKEN}"
      bar:
        npmRegistryServer: "https://npm.pkg.github.com/"
        npmAuthToken: "${GITHUB_TOKEN}"

    EOF

Miscellaneous

Other package managers may also support (or recommend) configuring the tokens in their own configuration files. Check their documentation for more information.