name: npm-trusted-publishing description: Publish npm packages via GitHub Actions with OIDC trusted publishing and provenance. Use when setting up automated npm publishes, debugging ENEEDAUTH/E404 errors, configuring provenance attestations, or setting up a new package for publication. metadata: category: user-invoked version: 2.0.0 disable-model-invocation: true
npm Trusted Publishing
Publish npm packages from GitHub Actions without long-lived tokens. Uses OIDC for authentication and generates provenance attestations automatically.
How It Works
- GitHub Actions workflow runs on tag push
- Workflow requests short-lived OIDC token from GitHub
- npm exchanges OIDC token for publish token via registry
- Package publishes with provenance attestation (supply-chain security)
- No
NPM_TOKENsecret needed — ever
Prerequisites
npmjs.org (one-time per package)
- Go to https://www.npmjs.com/settings/
- Packages → your package → Settings → Trusted publishing
- Provider: GitHub Actions
- Organization or user: matches GitHub username exactly (case-sensitive)
- Repository: matches repo name exactly (case-sensitive)
- Workflow filename:
publish.yml(exact — notpublish.yaml) - No environment name unless using GitHub deployment environments
package.json
{
"name": "@scope/package-name",
"version": "1.0.0",
"repository": {
"type": "git",
"url": "https://github.com/user/repo"
},
"publishConfig": {
"access": "public"
}
}
Requirements:
repository.urlmatcheshttps://github.com/<user>/<repo>(no.gitsuffix)- Package must be public (private packages cannot use provenance)
- Repository must be public (private repos cannot generate provenance)
Complete Working Workflow
name: Publish to npm
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Version to publish'
required: true
type: string
dry_run:
description: 'Dry run'
required: false
type: boolean
default: false
permissions:
contents: read
id-token: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Publish to npm
if: <!-- not a dry run -->
run: npm publish --access public --provenance
Node Version Note
| Node | Bundled npm | Trusted publishing |
|---|---|---|
| 18.x | 9.x | ❌ Not supported |
| 20.x | 10.x | ❌ Not supported |
| 22.x | 10.x | ❌ Use npx npm@11 publish |
| 24.x | 11.x | ✅ Works directly |
| 26.x | 11.x | ✅ Works directly |
For Node 20/22: Use npx npm@11 publish instead of npm publish.
Multi-Package Workspaces
For monorepos with multiple publishable packages:
- name: Publish core
working-directory: ./packages/core
run: npm publish --access public --provenance
- name: Publish toolkit
working-directory: ./packages/toolkit
run: npm publish --access public --provenance
Dry Run
Test without publishing:
- name: Dry run
run: npm publish --dry-run --access public --provenance
Provenance Verification
After publish:
# Confirm version is live
npm view @scope/package version
# Confirm provenance attestation exists
npm view @scope/package dist.attestations.provenance
# View full provenance details
npm view @scope/package --json | jq '.dist.attestations'
On npmjs.com package page, look for the "Provenance" badge.
Debugging Failed Publishes
Check npm version
npm --version # Must be 11.x for trusted publishing
Check OIDC env vars
- run: |
echo "ACTIONS_ID_TOKEN_REQUEST_URL: ${ACTIONS_ID_TOKEN_REQUEST_URL:+set}"
echo "ACTIONS_ID_TOKEN_REQUEST_TOKEN: ${ACTIONS_ID_TOKEN_REQUEST_TOKEN:+set}"
Check trusted publisher config
- Org/user, repo, and workflow filename are all case-sensitive
- npm does NOT validate the config on save
- Must match exactly:
https://github.com/Org/Repovshttps://github.com/org/repo
Common errors
| Error | Cause | Fix |
|---|---|---|
ENEEDAUTH |
No registry-url in setup-node |
Add registry-url: 'https://registry.npmjs.org' |
404 Not Found |
npm 10.x with trusted publishing | Use Node 24+ or npx npm@11 publish |
403 Forbidden |
Trusted publisher mismatch | Check case-sensitive org/repo/workflow |
EPUBLISHCONFLICT |
Version already exists | Bump version or check if already published |
Security Best Practices
- ✅ Use
permissions: id-token: write(minimal) - ✅ No
NPM_TOKENsecret needed - ✅ No
--provenanceflag needed (automatic on GitHub Actions) - ❌ Never commit
.npmrcwith auth tokens - ❌ Never use
secrets.NPM_TOKENin workflow - ❌ Never use long-lived publish tokens
Related
ci-cd-and-automation— General CI/CD patternsshipping-and-launch— Pre-launch checklistssecurity-and-hardening— Secrets management