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: publish
on:
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