Skip to content

Integrate OIDC Trusted Publisher auth and modernize CI workflows #206

Open

Description

@beauraines

Summary

Replace the long-lived NPM_TOKEN secret in the publish workflow with keyless OIDC-based authentication (npm Trusted Publishers), upgrade all GitHub Actions to current versions, add npm provenance attestation, and modernize the Node.js CI matrix.

This mirrors the changes made in bacon-ipsum-cli PR #58 and subsequent Node 24 commit.

Manual prerequisite (not automatable): Before merging, the repo owner must register the Trusted Publisher on npmjs.com under the package's Settings → Trusted Publisher, pointing to this repository and workflow filename (publish.yaml). The environment field should be left blank.

Background: How OIDC Trusted Publishers Work

npm's Trusted Publishers feature uses GitHub's OIDC (OpenID Connect) tokens to authenticate npm publish without any stored secrets. Here's how it works:

  1. The workflow requests an OIDC identity token from GitHub (enabled by permissions: id-token: write)
  2. actions/setup-node with registry-url: 'https://registry.npmjs.org' automatically configures npm to use this OIDC token when id-token: write is granted
  3. npm verifies the token against the Trusted Publisher configuration registered on npmjs.com
  4. No NPM_TOKEN secret is needed — the token is short-lived and scoped to the workflow run

Key requirement: Node.js >= 22.14 is required for OIDC provenance support in npm. We use Node 24 in the publish workflow.

The --provenance flag on npm publish generates a SLSA provenance attestation that cryptographically links the published package to its source repo and build.


Changes Required

1. Modify .github/workflows/publish.yaml

Note: This repo uses .yaml extension (not .yml) for the publish workflow.

Current file:

name: 'Publish to NPM'

on:
  workflow_run:
    workflows: ['Node.js CI']
    types: [completed]
    branches: [master,main]

jobs:
  publish-new-version:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: '0'
      - name: git setup
        run: |
          git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
      - name: setup node
        uses: actions/setup-node@v3
        with:
          node-version: 20.x
          registry-url: 'https://registry.npmjs.org'
      - name: npm install
        run: npm ci

      - name: Should release
        id: should_release
        continue-on-error: true
        run: npm run should-release -- -v

      - name: No release
        if: steps.should_release.outcome != 'success'
        run: echo "No release required. Skipping publishing."

      - name: Version bump
        if: steps.should_release.outcome == 'success'
        run: npm run release

      - name: Publish to NPM
        if: steps.should_release.outcome == 'success'
        run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: Push commits to GitHub
        if: steps.should_release.outcome == 'success'
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: ${{ github.ref }}
          tags: true
          force: true

Target file:

name: 'Publish to NPM'

on:
  workflow_run:
    workflows: ['Node.js CI']
    types: [completed]
    branches: [master,main]

permissions:
  contents: write
  id-token: write

jobs:
  publish-new-version:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: '0'
      - name: git setup
        run: |
          git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
      - name: setup node
        uses: actions/setup-node@v6
        with:
          node-version: '24'
          registry-url: 'https://registry.npmjs.org'
          cache: 'npm'
      - name: npm install
        run: npm ci

      - name: Should release
        id: should_release
        continue-on-error: true
        run: npm run should-release -- -v

      - name: No release
        if: steps.should_release.outcome != 'success'
        run: echo "No release required. Skipping publishing."

      - name: Version bump
        if: steps.should_release.outcome == 'success'
        run: npm run release

      - name: Publish to NPM
        if: steps.should_release.outcome == 'success'
        run: npm publish --provenance

      - name: Push commits to GitHub
        if: steps.should_release.outcome == 'success'
        uses: ad-m/github-push-action@v1.0.0
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: ${{ github.ref }}
          tags: true
          force: true

Summary of publish.yaml changes:

  • Add permissions: block at workflow level: contents: write + id-token: write
  • actions/checkout@v3 → actions/checkout@v6
  • actions/setup-node@v3 → actions/setup-node@v6
  • node-version: 20.x → node-version: '24'
  • Add cache: 'npm' to actions/setup-node
  • npm publish → npm publish --provenance
  • Remove the entire env: block with NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} from the Publish step
  • ad-m/github-push-action@master → ad-m/github-push-action@v1.0.0
  • Preserve existing force: true on the push action

2. Modify .github/workflows/test.yaml

Current file:

name: Node.js CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [18.x,20.x]

    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: npm run lint
      - run: npm run build --if-present
      - run: npm test

Target file:

name: Node.js CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

permissions:
  contents: read

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [20.x,22.x]

    steps:
      - uses: actions/checkout@v6
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v6
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npm run build --if-present
      - run: npm test

Summary of test.yaml changes:

  • Add permissions: contents: read at workflow level
  • actions/checkout@v3 → actions/checkout@v6
  • actions/setup-node@v3 → actions/setup-node@v6
  • Add cache: 'npm' to actions/setup-node
  • Node matrix [18.x,20.x] → [20.x,22.x] (drop EOL Node 18, add Node 22 LTS)
  • Preserve existing npm run lint and npm run build --if-present steps
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions