An npm publishing token is a non-human credential that can push code onto thousands of downstream machines, and stolen npm tokens were the engine of the largest JavaScript supply chain attacks of 2025. After the Nx s1ngularity and Shai-Hulud campaigns, npm removed classic tokens, capped the lifetime of write tokens, introduced stage-only tokens and pushed maintainers towards OIDC-based trusted publishing. This guide explains how identity and access now work on the public npm registry, which token type fits which job, how to lock down publishing access and what to monitor. It is written for package maintainers, platform and DevSecOps engineers, and the security teams who own the organisation's npm scopes and the CI pipelines that publish to them.
Key takeaways
- Classic tokens are gone. Creation was disabled on 5 November 2025 and every remaining classic token was permanently revoked by 9 December 2025; granular access tokens are now the only token type.
- Trusted publishing should be the default for releases. OIDC from GitHub Actions, GitLab CI/CD or CircleCI removes the long-lived publish secret that attackers stole in Nx and Shai-Hulud.
- Lock the door behind you. After configuring trusted publishing, set the package to "Require two-factor authentication and disallow tokens" so no granular token can publish it.
- Scope every remaining token narrowly. Read-only for installs, named packages instead of All Packages, CIDR ranges where the runner has fixed egress, and short expiry (write tokens are capped at 90 days).
- Plan for the next deadline. npm says direct publishing with granular tokens will be removed in January 2027, so token-based release pipelines need to move to trusted publishing or stage-only tokens now.
How identity and access work in npm
The public npm registry has three kinds of identity: user accounts (people, sometimes misused as bot accounts), organisations that own scoped packages such as @example/*, and workload identities from CI providers that npm trusts through OIDC. Every token is attached to a user account, and npm states that an access token "cannot have more permission than the user at any point in time". If the user loses access to a package or organisation, the token loses it too. That is useful at offboarding, but it also means a token issued by a maintainer with publish rights on 300 packages is, by default, a key to all 300.
Credential types on the registry today
| Credential | How it is issued | Lifetime | Typical use | Main risk |
|---|---|---|---|---|
Session token from npm login | Interactive login with 2FA | Two hours; not shown in token lists | Maintainer publishing from a workstation | Theft from a compromised laptop during the session |
| Granular token, read-only | npmjs.com Access Tokens page (CLI management announced December 2025) | Chosen at creation, at least one day | Installing private packages in CI | Exposure of private source code |
| Granular token, read and write (publish and stage) | As above | Maximum 90 days for write tokens | Legacy token-based release pipelines | Malicious versions published directly |
| Granular token, read and write (stage only) | As above | As above | Automation that prepares a release for a human to approve | Lower: a maintainer must review before publication |
| Trusted publisher (OIDC) | Short-lived OIDC token from a supported cloud CI provider, exchanged at publish time | Per job, no stored secret | Release workflows on GitHub-hosted runners, GitLab.com shared runners, CircleCI cloud | Compromise of the workflow itself |
| Classic token (automation, publish, read-only) | No longer issued | Revoked December 2025 | None | Stale copies in vaults and docs cause confusion, not access |
Granular token settings
- Packages and scopes: No access, Read-only, Read and write (publish and stage) or Read and write (stage only). Select up to 50 specific packages or scopes rather than All Packages.
- Organizations: No access, Read-only or Read and write. This covers organisation settings, teams and members only; it does not let the token publish packages the organisation owns. Up to 50 organisations per token.
- Allowed IP ranges: optional CIDR ranges. Useful for self-hosted runners and build hosts with fixed egress addresses; impractical on shared cloud runners.
- Expiration: at least one day in the future. Write tokens are capped at 90 days.
- Bypass two-factor authentication: off by default, write tokens only, and fixed at creation. When on, the token can publish without a 2FA challenge even where the account or package requires 2FA, unless the package disallows tokens.
- Limits: up to 1,000 granular tokens per account. The full token value is shown once, at creation.
Admin roles and privileged access
Privilege on npm sits in three places: organisation roles, team permissions on packages, and per-package maintainer and publishing settings. An attacker who controls any of them can publish.
| Role or setting | What it can do | Control to apply |
|---|---|---|
| Organisation owner | Billing, add and remove members, change roles, rename or delete the organisation, share org packages with other organisations | Two or three named owners, security keys, reviewed quarterly; the last owner cannot be removed, so plan succession |
| Organisation admin | Create and delete teams, manage team membership and team access to packages | Limit to platform owners; treat team changes as privileged events |
| Organisation member | Create and publish packages in the organisation scope | Do not grant broadly; use teams with read-only access for consumers |
| Package maintainer | Publish versions, change package settings including publishing access | Remove inactive maintainers |
| Publishing access setting | Decides whether tokens can publish | "Require two-factor authentication and disallow tokens" on every package that uses trusted publishing |
- Require 2FA for the organisation: owners can enforce it (they must have 2FA themselves). Members without 2FA are removed and must be re-invited after enrolling, so warn people before switching it on.
- Use security keys: npm supports WebAuthn security keys and platform authenticators, and the CLI 2FA mode
auth-and-writescovers both login and write operations. Prefer it overauth-only. - Protect recovery codes: store them in a password manager, not with the second factor. Using a recovery code places the account on a 72-hour hold during which it cannot publish or create tokens, which is a useful brake on takeover.
- Know what changed in August 2026: tokens with bypass 2FA can no longer change email or password, manage 2FA, create or manage tokens, or manage maintainers and teams. Those actions need an interactive 2FA challenge. Any automation that relied on doing them with a token will have broken and should be redesigned, not worked around.
Maintainer accounts are human identities with machine-scale blast radius. See the OWASP NHI Top 10 NHI10 Human Use of NHI Guide for why personal tokens should not run automation.
Integrations, API tokens and service accounts
Choosing a publishing pattern
| Situation | Recommended pattern | Why |
|---|---|---|
| Public package released from GitHub-hosted runners or GitLab.com shared runners | Trusted publishing, tokens disallowed on the package | No stored secret; provenance generated automatically for public repositories and packages |
| Released from CircleCI cloud | Trusted publishing | Supported, although npm does not generate provenance for CircleCI |
| Released from self-hosted runners | Stage-only granular token plus human approval, or move the release job to a hosted runner | npm trusted publishing does not support self-hosted runners at the time of writing |
| CI installs private packages | Read-only granular token scoped to the needed packages | Write access is never needed to install |
| Small team, few packages, manual releases | npm login session with security key, no stored token | Two-hour session, 2FA enforced at publish |
| Large organisation, hundreds of packages | Trusted publishing per repository, central inventory of publishers, stage-only tokens for exceptions | Removes token sprawl; exceptions are visible and reviewable |
Setting up trusted publishing safely
- Upgrade the release job to npm CLI 11.5.1 or later and Node 22.14.0 or later.
- Make sure the
repository.urlfield inpackage.jsonexactly matches the source repository, or publishing fails. - On npmjs.com, add the trusted publisher for the package: provider, owner, repository and workflow file name (and environment if you use one). npm does not validate the configuration when you save it, and a new configuration must complete its first publish within two days or it expires.
- In GitHub Actions grant
id-token: writeonly to the release job, not the whole workflow, and run it from a protected branch or tag with a deployment environment that requires approval. - Publish once and confirm the version shows provenance on the package page.
- Switch the package's publishing access to "Require two-factor authentication and disallow tokens". Trusted publishing keeps working because it uses OIDC, not a token.
- Delete the old
NPM_TOKENrepository or organisation secret and revoke the granular token behind it. - Rollback: if a release fails, existing trusted publisher entries cannot be edited, only deleted and recreated. Keep a documented break-glass path (interactive publish by a named maintainer with a security key) rather than re-enabling tokens.
Trusted publishing moves the trust boundary to the workflow. The Nx token was stolen through a pull_request_target workflow that echoed an attacker's pull request title, which contained shell commands, without sanitising it; the same flaw against an OIDC release job would let an attacker publish without stealing anything. Release workflows need the same scrutiny as the token they replace. See the GitHub and GitHub Actions Security Guide and the Workload Identity Federation Guide.
Tokens that remain
- Install tokens: keep the project
.npmrcline as//registry.npmjs.org/:_authToken=${NPM_TOKEN}and inject the value from the CI secret store. npm's own documentation says "Do not put a token in this file." - Separate install from release: jobs that run
npm installexecute third-party lifecycle scripts. Never give those jobs a write token or cloud credentials. Shai-Hulud ran from apreinstallscript and harvested whatever the runner could reach. Considernpm ci --ignore-scriptswhere the build allows it. The Tensorlake npm worm used the samepreinstallroute in 2026. - Ownership: every token needs a named owner, a purpose in its name and description, and a vault record. The token list shows only id, partial value, creation date and CIDR, so the context has to live in your inventory. See the API Key Management Guide.
- Bot accounts: if you must use a dedicated machine account, give it 2FA, membership only in the teams it needs, and no owner role.
Logging and detection
npm offers limited audit data to package owners, so detection relies on combining registry metadata, CI logs and your own inventory.
| Signal | Where to get it | What it can reveal |
|---|---|---|
| New version published without provenance on a package that normally has it | Registry metadata, package page, npm audit signatures | A token or interactive publish bypassing the release workflow, as attackers did with Nx |
| Publish with no matching CI run or release tag | Compare registry publish times with CI job logs | Out-of-band publishing with a stolen credential |
New or changed preinstall, install or postinstall scripts | Diff of package manifests between versions | Worm payloads such as Shai-Hulud and s1ngularity |
| Token inventory drift | npm token list per maintainer account, compared with the vault | Tokens created outside process, tokens without owners |
| Maintainer or team changes | Package settings, organisation member lists, account emails | Account takeover and persistence |
| Unexpected GitHub repositories, workflows or self-hosted runners | GitHub organisation audit log | Exfiltration repositories and runner persistence used by Shai-Hulud |
| Token values in code, logs or images | Secret scanning in repositories, CI logs and container images | Leaked tokens before an attacker uses them |
Map these to ATT&CK so they fit your detection backlog: T1528 Steal Application Access Token, T1552.001 Credentials In Files and T1195.002 Compromise Software Supply Chain.
Metrics that show it is working
- Trusted publishing coverage: percentage of published packages released through trusted publishing with tokens disallowed. Target 100% of public packages.
- Write tokens in existence: count of read and write granular tokens across maintainer accounts, trending to zero before January 2027.
- Provenance rate: share of new versions with a verified provenance attestation.
- Token hygiene: percentage of tokens with a named owner, a specific package list and expiry of 30 days or less.
- Time to revoke: minutes from a leak alert to
npm token delete, plus the up to one hour npm may take to complete revocation. - Maintainer review: percentage of packages whose maintainer list was reviewed in the last quarter.
Hardening baseline
- Inventory first: list every package and scope you own, its maintainers, its release workflow and every token that can touch it. Remove classic token references from vaults and runbooks; they no longer work and only add noise.
- Enforce 2FA: require 2FA for the organisation, move maintainers to security keys and
auth-and-writes, and confirm recovery codes are stored safely. - Pilot trusted publishing: start with two low-risk packages, verify provenance, then disallow tokens on them. Measure for two release cycles.
- Roll out by risk: packages with the most downloads or the most dependants next, then the long tail. Delete
NPM_TOKENsecrets as each package moves. - Constrain exceptions: self-hosted or unsupported pipelines use stage-only tokens with expiry of 30 days or less, specific packages, CIDR ranges and a named approver.
- Separate jobs: install and test jobs get read-only tokens and no cloud secrets; release jobs install nothing new and run only from protected refs.
- Consumer side: pin dependencies with a lockfile, use
npm ci, runnpm audit signatures, and restrict install scripts on CI runners. See the Malicious Package and Dependency Confusion Guide. - Rehearse revocation: run a tabletop where a maintainer laptop is compromised, and time how long it takes to revoke tokens, rotate CI secrets, deprecate bad versions and notify users.
Framework mapping
| Control area | NIST SP 800-53 Rev 5 | NIST CSF 2.0 | CIS Controls v8.1 | ISO/IEC 27001:2022 Annex A |
|---|---|---|---|---|
| Token issuance, expiry and storage | IA-5 Authenticator Management | PR.AA-01 | Control 5 Account Management | A.5.17 Authentication information |
| Scoped packages, roles and teams | AC-6 Least Privilege | PR.AA-05 | Control 6 Access Control Management | A.8.2 Privileged access rights |
| Disable install scripts and unused publish paths | CM-7 Least Functionality | PR.PS-01 | Control 16 Application Software Security | A.8.9 Configuration management |
| Provenance and signature verification | SR-11 Component Authenticity | GV.SC-05 | Control 16 Application Software Security | A.5.21 Managing information security in the ICT supply chain |
| Review of publish and token events | AU-6 Audit Record Review, Analysis, and Reporting | DE.AE-02 | Control 8 Audit Log Management | A.8.15 Logging |
Common misconfigurations
- Trusted publishing on, tokens still allowed: adding a trusted publisher does nothing to the old
NPM_TOKEN. Until the package disallows tokens, a stolen token still publishes. The Shai-Hulud npm Worm 2025 reached PostHog's packages by stealing exactly this kind of stored publishing secret from CI. - Publish token reachable from untrusted code: in the Nx s1ngularity Attack 2025, a
pull_request_targetworkflow with a read and writeGITHUB_TOKENlet an attacker rewrite the publish job and exfiltrateNPM_TOKEN, then publish around the release pipeline. - All Packages scope on a maintainer token: one token covering every package a maintainer can publish let worms republish up to 100 packages per stolen token. Specify packages, and split tokens by repository.
- Bypass 2FA as a convenience: turning on bypass 2FA for tokens used on laptops removes the only interactive check. Reserve it for CI exceptions, and never on packages that require fully enforced 2FA.
- Deleting the leak, not the token: removing an exfiltration repository or a committed
.npmrcdoes not revoke anything. More than 60% of npm tokens leaked in Shai-Hulud were still valid a week later. Revoke withnpm token deleteand confirm withnpm token list. - Forgotten maintainers: old contributors with publish rights are an untracked identity. The Mastra npm Supply Chain Attack 2026 began with a forgotten contributor account.
Practitioner checklist
- Inventory every package, maintainer, release workflow and granular token, with an owner for each.
- Require 2FA for the organisation and move maintainers to security keys with
auth-and-writes. - Configure trusted publishing for every package built on a supported hosted CI provider.
- Set "Require two-factor authentication and disallow tokens" on each package once trusted publishing works.
- Delete stored
NPM_TOKENsecrets for migrated packages and revoke the tokens behind them. - Limit remaining tokens to read-only or stage-only, named packages, CIDR ranges and short expiry.
- Keep write tokens and cloud credentials out of any job that runs
npm installon third-party code. - Alert on publishes without provenance or without a matching CI run, and on new install scripts.
- Review organisation owners, admins and package maintainers every quarter.
- Plan the move away from token-based direct publishing before npm removes it in January 2027.
Standards and references
- npm Docs: About access tokens
- npm Docs: Trusted publishing for npm packages
- npm Docs: Requiring 2FA for package publishing and settings modification
- npm Docs: Generating provenance statements
- npm Docs: Using private packages in a CI/CD workflow
- GitHub Changelog: npm classic tokens revoked, session-based auth and CLI token management now available
- GitHub Changelog: npm security update, classic token creation disabled and granular token changes
- MITRE ATT&CK: T1195.002 Compromise Software Supply Chain
Related NHI Mgmt Group resources: Software Supply Chain Security Guide · CI/CD Pipeline Identity Security Guide · SLSA and Build Provenance Guide · Docker Hub Tokens Security Guide · Secrets Management Guide