Skip to content

Update npm publishing to use provenance and granular access tokens #133

Description

@vlavrynovych

Context

npm has deprecated classic tokens in favor of more secure granular access tokens with automatic expiration (max 90 days). Manual publishing using classic tokens is no longer sustainable.

Current State

  • Publishing is done manually using classic tokens
  • .github/workflows/npm-publish.yml exists but not configured for automated publishing
  • Trusted publisher already configured on npm for the workflow

Proposed Solution

Update to modern npm publishing with provenance (cryptographic supply chain security):

1. Enable npm Provenance

Benefits:

  • ✅ Cryptographically signed packages
  • ✅ Build transparency (users can verify package was built by GitHub Actions)
  • ✅ npm shows "Provenance" badge on package page
  • ✅ No long-lived tokens stored in GitHub

2. Use Granular Access Token

  • Create granular token with 90-day expiration
  • Scope: @migration-script-runner/core (read/write)
  • Enable "Bypass 2FA" for CI/CD automation
  • Store in GitHub Secrets as NPM_TOKEN

3. Choose npm 2FA Setting

Select: "Require two-factor authentication or a granular access token with bypass 2fa enabled"

  • Allows automated publishing via GitHub Actions
  • Still requires 2FA for manual publishing
  • Compatible with provenance/OIDC

Implementation Tasks

  • Create granular access token on npmjs.com with "Bypass 2FA" enabled
  • Add NPM_TOKEN to GitHub repository secrets
  • Update package.json with publishConfig.provenance: true
  • Update .github/workflows/npm-publish.yml:
    • Add id-token: write permission
    • Add npm publish --provenance --access public
    • Ensure workflow runs on release
  • Test publishing workflow with a test release
  • Document token renewal process (90-day expiration)
  • Set calendar reminder for token renewal (every 80 days)

Updated Workflow Structure

name: Publish to npm

on:
  release:
    types: [published]

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
      - run: npm run build
      - run: npm publish --provenance --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Expected Outcome

After implementation:

  1. Releases are automatically published to npm when GitHub release is created
  2. Published packages show "Provenance" badge on npm
  3. Users can verify packages were built by official GitHub Actions workflow
  4. Token expires automatically after 90 days (security improvement)

References

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions