A reusable GitHub Actions workflow for publishing npm packages using OIDC (OpenID Connect) Trusted Publishing - no npm tokens required!
- ✅ No npm tokens - Uses OIDC authentication directly with npmjs.com
- ✅ Automatic provenance - npm automatically generates provenance attestations
- ✅ Enhanced security - Short-lived, workflow-specific credentials
- ✅ Simple setup - Minimal configuration required
- ✅ Public packages only - Designed for publishing public npm packages
Requires npm CLI v11.5.1 or later (installed in the workflow)
You must configure your package for Trusted Publishing before using this workflow:
-
Login to npmjs.com with a maintainer account
- Check maintainers:
npm view <package-name> - Login at: https://www.npmjs.com/login
- Check maintainers:
-
Navigate directly to package access settings
- Important: Use the direct link (search doesn't work reliably)
- Direct link format:
https://www.npmjs.com/package/<package-name>/access - Example: https://www.npmjs.com/package/@example/my-package/access
-
Add Trusted Publisher
- Scroll to "Trusted publishers" section
- Click "Add trusted publisher"
- Select provider: GitHub Actions
- Fill in:
- GitHub organization or user: Your GitHub org/username
- Repository: Your repository name
- Workflow filename: Your workflow filename (e.g.,
publish.yml- filename only, not full path) - Environment name: Leave blank (unless using GitHub Environments)
- Important: Check "npm publish" under "Allowed actions"
- Click "Add" or "Save"
-
Verify configuration
✓ GitHub Actions Organization: your-org Repository: your-repo Workflow: publish.yml Environment: (any) Allowed actions: npm publish
Choose a trigger that fits your release process.
Publish only when you explicitly decide to — useful when not every merge to main should cut a release.
name: Publish to npm
on:
workflow_dispatch:
jobs:
publish:
uses: mapbox/gha-public/.github/workflows/workflow-npm-oidc-publish.yml@main
with:
npm-tag: 'latest'Publish automatically on every PR merge. Best suited for projects where every merge is a releasable change and the version is always bumped in the PR.
name: Publish to npm
on:
push:
branches:
- main
jobs:
publish:
uses: mapbox/gha-public/.github/workflows/workflow-npm-oidc-publish.yml@main
with:
npm-tag: 'latest'Note:
npm publishwill fail if the version inpackage.jsonhas already been published. Make sure the version is bumped before merging.
| Input | Description | Required | Default |
|---|---|---|---|
node-version |
Node.js version to use | No | '22' |
working-directory |
Working directory for the package | No | '.' |
npm-tag |
npm dist-tag to use | No | 'latest' |
run-tests |
Run npm test before publishing | No | false |
dry-run |
Perform a dry-run publish | No | false |
create-github-release |
Create a GitHub release with auto-generated notes after publishing. Skipped on dry-run. Fails if the version tag already exists. | No | false |
| Output | Description |
|---|---|
package-name |
The published package name |
package-version |
The published package version |
name: Publish to npm
on:
push:
branches:
- main
jobs:
publish:
uses: mapbox/gha-public/.github/workflows/workflow-npm-oidc-publish.yml@main
with:
create-github-release: trueRelease notes are auto-generated by GitHub from merged PRs since the previous release tag. Docs
The release tag is derived from the version field in package.json (e.g. 1.2.3 → v1.2.3).
The workflow fails if that tag already exists — bump the version before merging.
name: Publish Package A
on:
workflow_dispatch:
jobs:
publish-a:
uses: mapbox/gha-public/.github/workflows/workflow-npm-oidc-publish.yml@main
with:
working-directory: './packages/package-a'
npm-tag: 'latest'name: Test Publish
on:
pull_request:
jobs:
test-publish:
uses: mapbox/gha-public/.github/workflows/workflow-npm-oidc-publish.yml@main
with:
dry-run: trueYour package.json should include:
{
"name": "@your-scope/your-package",
"version": "1.0.0",
"scripts": {
"build": "your-build-command", // Optional, runs if present
"test": "your-test-command" // Required if run-tests: true
}
}For scoped packages to be public, ensure:
{
"publishConfig": {
"access": "public"
}
}Cause: npm rejected the OIDC token - Trusted Publisher configuration doesn't match
Solution:
- Verify organization/repository name is correct (case-sensitive)
- Check workflow filename matches exactly (filename only, not path)
- Ensure "npm publish" is checked under Allowed actions
- Verify package is configured as public (not private)
Cause: npm couldn't match your workflow to the Trusted Publisher config
Solution: Double-check all configuration values match exactly
Cause: Missing id-token: write permission
Solution: This workflow includes the permission - ensure you're not overriding it