告别 NPM Token:迁移到 Trusted Publishing 全记录

3 mins to read

发生了什么?

2025 年 12 月 9 日,npm 正式永久撤销了所有 Classic Token。与此同时,npm 引入了基于会话的认证(2 小时有效期)和新的 CLI Token 管理方式。这意味着:

  • 所有 Classic Token 立即失效,无法恢复
  • npm login 现在只发放 2 小时的短期会话令牌
  • CI/CD 场景推荐使用 Granular Access TokenOIDC Trusted Publishing

我的 GitHub 项目 CI 工作流自然也挂了 —— 因为 Secrets 里存的还是那个已被吊销的 Classic Token。

Trusted Publishing 是什么?

简单说,Trusted Publishing 利用 OIDC(OpenID Connect) 协议,让 CI/CD 平台(如 GitHub Actions)直接与 npm 交换短期凭证来发布包,完全不需要存储任何 npm token

优势很明显:

  • 没有长期 Token,不存在泄露风险
  • 自动附带 Provenance(来源证明),包的构建来源可追溯
  • 配置一次,后续发版无需维护密钥

目前支持 GitHub Actions(GitHub-hosted runners)和 GitLab CI/CD(shared runners),不支持自托管 runner。

版本要求:npm CLI ≥ 11.5.1,Node.js ≥ 22.14.0。

迁移步骤

1. 在 npmjs.com 配置 Trusted Publisher

进入目标 Package 的 Setting 页面,找到 Trusted Publisher 部分,填写:

  • Owner:GitHub 用户名或组织名
  • Repository:仓库名
  • Workflow filename:工作流文件名(含 .yml 扩展名,如 publish.yml
  • Environment(可选):GitHub 部署环境名

注意:文件名、仓库名区分大小写,必须与 workflow 文件完全一致。

2. 配置 GitHub Actions Workflow

核心改动就两点:声明 OIDC 权限、直接用 npm publish

以下是我最终可用的 workflow(使用 pnpm):

name: publish

on:
  release:
    types: [published, released]
  workflow_dispatch:

jobs:
  publish:
    runs-on: ubuntu-latest

    permissions:
      id-token: write  # OIDC 必需
      contents: read

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 10

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 24
          registry-url: 'https://registry.npmjs.org'
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --no-frozen-lockfile

      - name: Build
        run: pnpm run build

      - name: Publish
        if: github.repository == 'wayjam/picgo-plugin-s3'
        run: pnpm publish --no-git-checks --ignore-scripts --provenance --access public

不再需要 NPM_TOKEN、不再需要 NODE_AUTH_TOKEN,也不再需要 JS-DevTools/npm-publish 这类第三方 Action。直接原生 npm publish / pnpm publish 就行。

注意事项与故障排除

package.json 必须包含 repository 字段

npm 的 Provenance 机制会校验发布环境与包元数据是否匹配。如果 package.json 中没有 repository 字段(或值为空),会返回 422 错误:

npm error 422 Unprocessable Entity - PUT https://registry.npmjs.org/<package-name>
- Error verifying sigstore provenance bundle: Failed to validate repository information:
  package.json: "repository.url" is "", expected to match
  "https://github.com/<owner>/<repo>" from provenance

确保 package.json 中有正确且与 npm 配置一致的 repository 字段:

"repository": {
  "type": "git",
  "url": "git+https://github.com/<owner>/<repo>.git"
}

pnpm 添加 --no-git-checks(可选)

pnpm publish 默认会执行 git 检查,拒绝从非主分支或脏工作区发布。而 GitHub Actions 的 actions/checkout 默认是 Detached HEAD 状态,会直接触发该检查失败。如果是非 main/master 发布则需加上这个参数:

pnpm publish --no-git-checks --ignore-scripts --provenance --access public

Workflow 必须显式声明权限

没有在 job 级别声明 permissions,OIDC token 交换会直接失败。以下两项缺一不可:

permissions:
  id-token: write  # 用于 OIDC token 交换
  contents: read   # 用于 checkout 代码

报错 “Access token expired or revoked”

看到这个错误不一定是 token 问题。更常见的原因是:npm 网页端填写的 workflow 文件名与实际文件名不一致(包括大小写、.yml/.yaml 扩展名),导致 OIDC 验证时仓库/workflow 信息比对失败。逐项核对 npm 设置中的 owner、repo、workflow filename 即可。

清理

配置成功并验证发布正常后:

  1. 从 GitHub 仓库的 Settings → Secrets 中删除旧的 NPM_TOKEN
  2. 如果之前用的是 JS-DevTools/npm-publish,可以从 workflow 中移除该依赖
  3. 在 npm 包设置中,可以启用 “Require two-factor authentication and disallow tokens” 进一步加固安全

参考