Skip to content

Migrate npm-publish.yml to trusted publishing (OIDC), drop NPM_TOKEN #2016

Description

@alexwarren

Summary

npm-publish.yml currently authenticates to npm using a long-lived granular access token (NPM_TOKEN repo secret) with 2FA bypass. npm/GitHub are deprecating that bypass in two phases (see changelog):

  • Phase 1 (early August 2026, already in effect): 2FA-bypass tokens can no longer create/delete tokens, change account/password/email/2FA settings, change package access/maintainers/trusted-publishing config, or manage org/team membership.
  • Phase 2 (~January 2027): 2FA-bypass tokens lose direct publishing ability entirely — reduced to "reading private packages and staging a publish, where a package only becomes public after a human 2FA approval."

We already hit Phase 1 in practice: the very first CI publish of @textadventures/quest-viva-wasmplayer failed with npm error code EOTP because creating the package for the first time needed to establish maintainer/ownership (a Phase-1-restricted action even for a bypass-capable token). It was bootstrapped with a one-off manual npm publish from a local machine with real 2FA instead. Ordinary version-bump publishes from CI should be unaffected until Phase 2 lands, but Phase 2 will break them too.

What to do

Migrate npm-publish.yml to npm's trusted publishing (OIDC) instead of a long-lived token:

  1. On the package's npmjs.com settings page, add a Trusted Publisher: GitHub Actions, repo textadventures/quest, workflow file npm-publish.yml (no environment).
  2. Update .github/workflows/npm-publish.yml:
    • Add permissions: id-token: write (and contents: read) to the job.
    • Drop the NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} env var from the "Publish to npm" step — npm's CLI auto-detects the GitHub Actions OIDC environment once a trusted publisher is configured, no token needed.
  3. Once confirmed working on a real release, remove the now-unused NPM_TOKEN repo secret.

This sidesteps the whole 2FA-bypass-token question going forward and isn't affected by the Phase 2 deprecation.

Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions