npm Trusted Publishing: Secure CI/CD with OIDC
Stop storing npm tokens in your CI secrets. Trusted Publishing uses OpenID Connect (OIDC) to let GitHub Actions and GitLab CI publish packages directly to npm — no long-lived tokens required.
Why use Trusted Publishing?
Traditional npm publishing from CI requires storing an npm automation token as a secret. This creates security risks:
Traditional Tokens
- -Long-lived (never expire by default)
- -Can be stolen from CI logs or config
- -Require manual rotation
- -Broad permissions
- -No audit trail of which workflow published
Trusted Publishing (OIDC)
- +Short-lived (expire after each publish)
- +Never stored anywhere
- +Automatic rotation
- +Scoped to specific workflows
- +Full provenance attestation
Provenance attestation: When you use Trusted Publishing, npm automatically generates a signed attestation linking the published package to its source repository and build. Users can verify this at npmjs.com/package/your-package under "Provenance".
GitHub Actions setup
Configure trusted publisher on npm
Go to npmjs.com → your package → Settings → Trusted Publisher.
Click GitHub Actions and enter:
- Owner: Your GitHub username or organization
- Repository: The repository name (without owner)
- Workflow: The workflow filename (e.g.,
publish.yml) - Environment: Optional, but recommended (e.g.,
npm)
Click Save.
Create or update your workflow
Create .github/workflows/publish.yml:
name: Publish to npm
on:
release:
types: [created]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # Required for OIDC
environment: npm # Optional: match your npm config
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm publish --provenance --access publicKey points:
id-token: writepermission is required for OIDC- No
NODE_AUTH_TOKENneeded — npm handles authentication automatically --provenanceadds signed attestation (automatic with OIDC)
Remove old npm token (if exists)
Go to your repository's Settings → Secrets and variables → Actions and delete any NPM_TOKEN or NODE_AUTH_TOKEN secrets. They're no longer needed.
Important: The workflow filename must match exactly what you configured on npmjs.com, including the .yml extension. All fields are case-sensitive.
GitLab CI setup
Configure trusted publisher on npm
Go to npmjs.com → your package → Settings → Trusted Publisher.
Click GitLab CI/CD and enter:
- Namespace: Your GitLab username or group
- Project: The project name
- Environment: The GitLab environment name (e.g.,
production)
Update your .gitlab-ci.yml
publish:
stage: deploy
image: node:20
id_tokens:
SIGSTORE_ID_TOKEN:
aud: https://registry.npmjs.org
environment: production
script:
- npm ci
- npm publish --provenance --access public
rules:
- if: $CI_COMMIT_TAGThe id_tokens block configures OIDC token generation with the npm registry audience.
Monorepo setup
Trusted Publishing works with monorepo tools. Each package needs its own trusted publisher configuration on npm.
With Lerna
Lerna 7+ has built-in OIDC support. Use the standard workflow:
- run: npx lerna publish from-git --yes
See Lerna's OIDC documentation for details.
With Changesets
Changesets uses npm's native publishing, so OIDC works automatically:
- run: npx changeset publish
With pnpm
- run: pnpm -r publish --access public
Remember: Configure a trusted publisher for each package in your monorepo on npmjs.com. They can all point to the same workflow file.
Troubleshooting
"Unable to authenticate" error
- • Verify the workflow filename matches exactly (including
.yml) - • Check that owner/repository are correct and case-sensitive
- • Ensure
id-token: writepermission is set - • Confirm npm CLI is version 11.5+ for full OIDC support
"Repository URL mismatch" error
Your package.json must have a repository.url field matching your GitHub/GitLab URL:
{
"repository": {
"type": "git",
"url": "https://github.com/owner/repo.git"
}
}Publish works locally but fails in CI
OIDC only works in CI environments. For local publishing, you still need a traditional npm token or to use npm login.
Self-hosted runners
Trusted Publishing currently only supports cloud-hosted runners. Self-hosted runner support is planned for a future release. For now, use traditional tokens on self-hosted runners.
The npm UX problem
Setting up Trusted Publishing sounds straightforward, but npm's UX makes it tedious — especially for multiple packages.
The catch: you can't create new packages with OIDC
npm requires a package to already exist before you can configure Trusted Publishing. That means your first publish must use a traditional token — there's no way around it.
For each new package, you'll need to create a temporary token, publish once, then go through the OIDC setup.
The 2FA gauntlet (per package)
After the initial publish, enabling OIDC-only requires multiple steps — each requiring a separate 2FA code:
Create npm token, publish first version
2FA code #1 (token creation) + #2 (publish)
Navigate to package settings
2FA code #3
Add GitHub/GitLab repository + workflow
2FA code #4
Toggle radio button to require OIDC only
2FA code #5 — easy to miss!
Delete the temporary token you created
2FA code #6
That's 6 two-factor authentication codes per package. For a monorepo with 10 packages, you're looking at 60 codes just to set up OIDC correctly.
The radio button trap
The most critical step — enabling "Require OIDC" to disable token-based publishing — is a small radio button that's easy to overlook. Many developers add the trusted publisher but never toggle this option, leaving their packages still vulnerable to token-based attacks.
Monitoring your publishes
With Trusted Publishing, every publish generates a provenance attestation. But you still receive npm's "Successfully published" emails — one per package, per version.
These emails show whether the publish was "via OIDC" (Trusted Publishing) or "via token". This is your signal for detecting misconfigured packages — if you see "via token" on a package that should be OIDC-only, someone missed the radio button step.
Why monitoring matters
- •Catch misconfigurations: Spot packages still accepting token-based publishes when they should be OIDC-only
- •Detect suspicious activity: An unexpected "via token" publish could indicate credential compromise
- •Audit trail: Keep records of who published what, when, and how for compliance
Consolidate notifications with npmDigest
Instead of 20+ emails per release, get a single digest showing all publishes — with OIDC vs token status, versions, and timestamps. Instantly spot packages where you forgot to enable OIDC-only, or detect unexpected token-based publishes that could signal a security issue.
Frequently asked questions
What is npm Trusted Publishing?
Trusted Publishing uses OpenID Connect (OIDC) to let CI/CD systems publish packages to npm without storing long-lived tokens. Your CI provider (GitHub Actions, GitLab CI) authenticates directly with npm using short-lived, workflow-specific credentials.
Why should I use Trusted Publishing instead of npm tokens?
Traditional npm tokens are long-lived secrets that can be stolen, leaked, or accidentally committed. Trusted Publishing eliminates these risks — tokens are generated on-demand, scoped to specific workflows, and expire automatically after each publish.
Which CI providers support npm Trusted Publishing?
Currently, npm Trusted Publishing supports GitHub Actions and GitLab CI/CD. Both use OIDC to authenticate with npm's registry. Self-hosted runners are not yet supported.
Do I need to update my workflow to use Trusted Publishing?
Yes, but minimally. You need to: (1) Configure the trusted publisher on npmjs.com, (2) Add 'id-token: write' permission to your workflow, (3) Remove NPM_TOKEN from your secrets and npm publish command. The npm CLI handles the rest automatically.
Can I use Trusted Publishing with Lerna or Changesets?
Yes. Lerna has built-in OIDC support via 'lerna publish --registry'. Changesets works with npm's native OIDC when you use 'changeset publish'. Both tools respect npm's authentication configuration.
Related guides
- Monorepo Publishing Guide — Lerna, Turborepo, and Changesets setup
- npm Publish Security — Monitor and audit package releases
- Stop npm Email Notifications — Filter or digest your publish emails