npmパッケージ公開のOIDC Trusted publishingへの移行
Classic tokenの廃止と代替手段
2025年12月9日、npmのClassic tokenが完全に廃止された(npm classic tokens revoked, session-based auth and CLI token management now available)。
代わりにローカルではnpm loginによるセッションベースの認証、GitHub ActionsなどのCI/CD環境では2FAをBypassしたGranular Access TokensまたはOIDC Trasted publishingを利用できる。
Trasted publishingのメリット
npm公式がTrasted publishingを推奨している(Security best practices: Prefer trusted publishing over tokens)。
Trasted publishingは、OpenID Connect (OIDC) を利用しGitHub Actionsとnpmが直接信頼関係を結ぶ仕組みである。 実際、CI/CD環境においてTrasted publishingはTokenを用いた方法の上位互換と言える。
Trasted publishingにはTokenと比較して次の特性がある:
- 漏洩リスクがない:Secretが存在せず、CI実行時に短命なトークンが発行されるのみ。
- 管理コストがない:定期的なトークン再発行やSecretのローテーションをする必要がない
Trusted Publishingへの移行
STEP 1: npmパッケージ設定の変更
まず、npmでパッケージのSettingsからTrusted Publisherを登録する。
- npm公式サイトにログインし、対象パッケージのページへ移動。
- Settingsタブをクリックし、Trusted PublisherにあるSelect your publisherから利用するCI/CD環境を選択する。
- GitHub Actionsを選択した場合、以下の情報を入力する。
- Organization or user: GitHub Actionsを実行するリポジトリのOrganizationまたはUser名
- Repository: GitHub Actionsを実行するリポジトリ名
- Workflow filename: npm publishを実行するワークフローファイル名
- Environment name: GitHub Actions environmentを利用している場合は環境名を指定
この設定により、「指定したリポジトリ」の「指定したワークフローファイル」からのみ、Publish権限が発行されるようになる。

STEP 2: GitHub Actionsワークフローの修正
次に、GitHub ActionsのYAMLファイルを修正する。主な変更点は permissions の追加と env の削除。
修正前の例:
jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '24.x' registry-url: 'https://registry.npmjs.org' - run: npm publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} # ← 削除修正後の例:
jobs: publish: runs-on: ubuntu-latest permissions: contents: read id-token: write # ← OIDCトークン発行のために必須 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: # npm v11.5.1以上が必要 # Node.js v20やv22、古い特定のv24バージョンを利用している場合、別途npmの更新が必要 node-version: '24.x' registry-url: 'https://registry.npmjs.org'# - run: npm install -g npm@latest - run: npm publishTrasted publishingの制約
npmのTrasted publishingは、現状いくつか制約がある。
- Trasted publisherが1つに限定される。
- 特定のリポジトリの特定のファイルからのみしか利用できない。
- プライベートリポジトリではprovenanceを生成できない。
npm publish --provenanceを実行するとpublishに失敗する。
- Trasted publisherとpackage.jsonのrepositoryフィールドの一致が必要
- repositoryが未記入または一致しない場合、422 Unprocessable Entityエラーが発生する。
"repository": "https://github.com/orgs/repository-name.git"といった指定が必要。- これはprovenanceを生成しない場合でも発生する。
- npm v11.5.1以上が必要。
- 最新のNode.js v24以上であれば問題はないが、それ未満では別途npmの更新が必要。
- それと分かるエラーは出ないため注意。遭遇した例では404エラーが発生した。
- 利用できるCI/CD環境はnpmが対応している環境に限定される。
GitHub Actionsワークフローの実装例
デフォルトブランチにコミットが行われる度に、可能ならリリースまたはアルファリリースを行うリポジトリの実装例。 Trashed publisherは単一のワークフローファイルに紐付ける必要があるため、Trasted publishingを行う場合リリースとアルファリリースでワークフローを分けることは出来ない。
name: publishon: push: branches: - main
concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true
env: CI: true
permissions: contents: read id-token: write
jobs: check: runs-on: ubuntu-latest timeout-minutes: 3 outputs: can_publish: ${{ steps.can-publish.outputs.can_publish }} steps: - uses: actions/checkout@v5 - uses: actions/setup-node@v5 with: node-version: "24.x" registry-url: "https://registry.npmjs.org" - name: Install dependencies run: yarn install --frozen-lockfile - name: Build package run: yarn build - name: Check if can publish id: can-publish run: | npx can-npm-publish --verbose && echo "can_publish=true" >> $GITHUB_OUTPUT || echo "can_publish=false" >> $GITHUB_OUTPUT continue-on-error: true - uses: actions/upload-artifact@v4 with: name: dist path: dist retention-days: 1
publish-release: needs: check if: needs.check.outputs.can_publish == 'true' runs-on: ubuntu-latest timeout-minutes: 3 steps: - uses: actions/checkout@v5
- uses: actions/setup-node@v5 with: node-version: "24.x" registry-url: "https://registry.npmjs.org" - uses: actions/download-artifact@v4 with: name: dist path: dist - name: Publish run: | npm publish
publish-alpha: needs: check if: needs.check.outputs.can_publish != 'true' runs-on: ubuntu-latest timeout-minutes: 3 steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - uses: actions/setup-node@v5 with: node-version: "24.x" registry-url: "https://registry.npmjs.org" - uses: actions/download-artifact@v4 with: name: dist path: dist - name: Update version run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" yarn version --prepatch --preid alpha-$(($(date +%s%N)/1000000))-$(git rev-parse --short HEAD) - name: Publish run: | npm publish --tag alpha