🚨 npm Classic Tokens Being Revoked November 19: Complete Migration Guide
Table of Contents
What's Happening
npm has disabled the creation of classic tokens and will revoke ALL existing classic tokens on November 19, 2025. This is a mandatory security upgrade affecting millions of developers and automated workflows.
Why This Change?
- Security: Classic tokens had overly broad permissions with no scoping capabilities
- Granularity: New tokens support package-specific and permission-specific access
- Auditability: Granular tokens provide better audit trails and can be rotated per-package
- Provenance: Trusted publishing enables cryptographic proof of package origin
⚠️ Impact on Your Workflows
If you use classic tokens in:
- GitHub Actions workflows (
.github/workflows/*.yml) - GitLab CI/CD pipelines (
.gitlab-ci.yml) - CircleCI, Travis CI, Jenkins, or other CI systems
- Local
.npmrcfiles - Docker build processes
- Automated deployment scripts
All these workflows will FAIL after November 19 unless you migrate.
Timeline & Deadlines
npm disabled creation of new classic tokens
7 days remaining to migrate
ALL classic tokens will be revoked
Classic tokens no longer function. Workflows using them will fail.
Check If You're Affected
1Check npm Access Tokens
Log in to npm and visit your access tokens page:
# Visit: https://www.npmjs.com/settings/YOUR_USERNAME/tokens
# Or use npm CLI to list tokens
npm token list
Look for tokens marked as "Legacy Token" or "Read and Publish" (legacy format).
2Check CI/CD Secrets
Search your repositories for npm token usage:
# Search for NPM_TOKEN in GitHub Actions
grep -r "NPM_TOKEN" .github/workflows/
# Search in package.json scripts
grep -r "NPM_TOKEN" package.json
# Search in .npmrc files
find . -name ".npmrc" -exec grep "authToken" {} \;
3Check Local Configuration
# Check global .npmrc
cat ~/.npmrc
# Check project .npmrc
cat .npmrc
# Look for lines like:
# //registry.npmjs.org/:_authToken=npm_XXXXXXXXXXXX
Migration Options
Option 1: Granular Access Tokens
Fine-grained tokens with package-specific permissions
- Package-specific scoping
- Read-only, publish, or admin permissions
- Works with all CI/CD systems
- Easy to rotate per package
Option 2: Trusted Publishing
Zero-token authentication with provenance
- No tokens to manage or rotate
- Cryptographic proof of origin
- Automatic provenance attestations
- GitHub Actions only (for now)
Recommendation
If you use GitHub Actions, migrate to Trusted Publishing for zero-token security. For all other CI systems or multi-platform needs, use Granular Access Tokens.
Granular Access Tokens Setup
1Create a Granular Access Token
-
1. Go to npm Access Tokens:
https://www.npmjs.com/settings/YOUR_USERNAME/tokens - 2. Click "Generate New Token" → "Granular Access Token"
-
3. Configure Token Settings:
- • Token Name:
my-package-publish-token - • Expiration: Custom (e.g., 1 year) or No expiration
- • Packages and scopes: Select specific packages
- • Organizations: Select if applicable
- • Permissions: Choose "Read and write"
- • Token Name:
2Add Token to CI/CD Secrets
GitHub Actions:
# Go to: Repository Settings → Secrets and variables → Actions
# Click "New repository secret"
# Name: NPM_TOKEN
# Value: npm_xxxxxxxxxxxxxxxxxxxx (paste your granular token)
GitLab CI:
# Go to: Project Settings → CI/CD → Variables
# Click "Add variable"
# Key: NPM_TOKEN
# Value: npm_xxxxxxxxxxxxxxxxxxxx
# ✓ Mask variable
# ✓ Protect variable (optional)
3Update CI/CD Configuration
Your existing CI/CD workflows should continue working with granular tokens using the same NPM_TOKEN environment variable:
# .github/workflows/publish.yml
name: Publish to npm
on:
release:
types: [created]
jobs:
publish:
runs-on: ubuntu-latest
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 test
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Migration Complete!
Simply replace your classic token secret with the new granular token. No code changes needed!
Trusted Publishing (Provenance)
Trusted publishing eliminates tokens entirely by using OpenID Connect (OIDC) to authenticate GitHub Actions directly with npm. This provides cryptographic proof of where your package came from.
Benefits of Trusted Publishing
- ✅ Zero secrets - No NPM_TOKEN to manage or rotate
- ✅ Automatic provenance - Cryptographic attestations added to every publish
- ✅ Supply chain security - Users can verify package origin
- ✅ No token leaks - Nothing to accidentally commit or expose
1Enable Trusted Publishing on npm
-
1. Go to your package settings on npm:
https://www.npmjs.com/package/YOUR_PACKAGE_NAME/access - 2. Scroll to "Publishing access" section
- 3. Click "Add trusted publisher"
-
4. Configure GitHub Actions connection:
- • Repository owner:
your-github-username - • Repository name:
your-repo-name - • Workflow name:
publish.yml(optional - restricts to specific workflow) - • Environment:
production(optional - restricts to specific environment)
- • Repository owner:
2Update GitHub Actions Workflow
# .github/workflows/publish.yml
name: Publish to npm with Provenance
on:
release:
types: [created]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # REQUIRED for provenance
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 test
# Publish with provenance - NO TOKEN NEEDED!
- run: npm publish --provenance --access public
# NO env: NODE_AUTH_TOKEN needed!
# GitHub OIDC handles authentication automatically
⚠️ Important: Remove NPM_TOKEN Secret
After migrating to trusted publishing, you can safely delete the NPM_TOKEN secret from your repository. The workflow no longer needs it!
3Verify Provenance
After publishing with provenance, users can verify your package origin:
# Check if package has provenance
npm view your-package-name dist.integrity
# Audit package provenance
npm audit signatures
Packages with provenance show a verified badge on npm:
CI/CD Configuration Examples
GitLab CI
# .gitlab-ci.yml
publish:
stage: deploy
image: node:20
only:
- tags
script:
- echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
- npm ci
- npm test
- npm publish
variables:
NPM_TOKEN: $NPM_TOKEN # Set in GitLab CI/CD Variables
CircleCI
# .circleci/config.yml
version: 2.1
jobs:
publish:
docker:
- image: cimg/node:20.10
steps:
- checkout
- run:
name: Authenticate with npm
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc
- run: npm ci
- run: npm test
- run: npm publish
workflows:
publish-on-tag:
jobs:
- publish:
filters:
tags:
only: /^v.*/
branches:
ignore: /.*/
Jenkins Pipeline
// Jenkinsfile
pipeline {
agent any
environment {
NPM_TOKEN = credentials('npm-token-credential-id')
}
stages {
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Test') {
steps {
sh 'npm test'
}
}
stage('Publish') {
when {
tag "v*"
}
steps {
sh '''
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
npm publish
'''
}
}
}
}
Travis CI
# .travis.yml
language: node_js
node_js:
- '20'
deploy:
provider: npm
email: your-email@example.com
api_token: $NPM_TOKEN
on:
tags: true
before_deploy:
- npm ci
- npm test
Azure Pipelines
# azure-pipelines.yml
trigger:
tags:
include:
- v*
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
inputs:
versionSpec: '20.x'
displayName: 'Install Node.js'
- script: |
npm ci
npm test
displayName: 'Install and Test'
- script: |
echo "//registry.npmjs.org/:_authToken=$(NPM_TOKEN)" > .npmrc
npm publish
displayName: 'Publish to npm'
env:
NPM_TOKEN: $(NPM_TOKEN)
Troubleshooting
Error: "You must be logged in to publish packages"
Cause: Token not properly configured in .npmrc or environment
Solution:
# Ensure .npmrc exists with:
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
# Or set NODE_AUTH_TOKEN environment variable
export NODE_AUTH_TOKEN=$NPM_TOKEN
Error: "403 Forbidden - you do not have permission to publish"
Cause: Granular token doesn't have write access to the package
Solution:
- Go to npm token settings and edit the token
- Ensure the package is included in "Packages and scopes"
- Ensure permission is set to "Read and write"
Error: "npm publish --provenance failed"
Cause: Missing id-token: write permission in workflow
Solution:
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # ADD THIS LINE
steps:
# ...
Multiple packages in monorepo
Solution: Create one granular token per package, or use organization-scoped tokens:
# Create tokens with package-specific access
# Token 1: @myorg/package-a (Read and write)
# Token 2: @myorg/package-b (Read and write)
# Or create one org-scoped token with write access to all packages
# Packages and scopes: @myorg/* (All packages)
Quick Reference
Granular Token Checklist
- ☐ Create granular access token on npm
- ☐ Add NPM_TOKEN to CI/CD secrets
- ☐ Verify token has write permissions for your packages
- ☐ Test publish in CI/CD
- ☐ Delete old classic token
Trusted Publishing Checklist
- ☐ Configure trusted publisher on npm package settings
- ☐ Add
id-token: writepermission to workflow - ☐ Add
--provenanceflag to npm publish - ☐ Remove NPM_TOKEN from workflow
- ☐ Delete NPM_TOKEN secret from repository
- ☐ Test publish and verify provenance badge
Conclusion
The npm classic token deprecation is a mandatory security upgrade that affects the entire JavaScript ecosystem. With just 7 days until the November 19 deadline, immediate action is required.
✅ Next Steps
- 1. TODAY: Audit all repositories and CI/CD pipelines for classic token usage
- 2. THIS WEEK: Migrate to granular tokens or trusted publishing
- 3. BEFORE NOV 19: Test all automated workflows
- 4. NOV 19+: Delete classic tokens (npm will do this automatically)
For most teams using GitHub Actions, trusted publishing with provenance is the recommended path forward. It eliminates token management entirely while providing supply chain security benefits.