npm / npm/cli

[BUG] Root preinstall cannot update registry credentials for the active npm install

Open
#9,853 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Bug
Dominant language
JavaScript
Stars
10.1k
Forks
4.7k
Avg merge
2d 2h
Merged PRs (30d)
19

Description

Is there an existing issue for this?
  • I have searched the existing issues
This issue exists in the latest npm version
  • I am using the latest npm
This is not just a request to bump a dependency for a CVE
  • This is not solely a request to bump a dependency for a CVE
Current Behavior

With npm 12, a root preinstall script runs before npm fetches project dependencies. However, npm loads the user and project .npmrc files before running that script and does not use credential changes made by the script for the active installation.

npm/cli#2660 was a long-running request to execute root preinstall before dependency installation. A major use case raised repeatedly in that discussion was automatic authentication for private registries, including registries that issue short-lived tokens. npm 12 now provides the requested lifecycle ordering, but that use case remains impossible within a single npm install: the earlier configuration load means credentials created or refreshed by preinstall are not used by the dependency requests that follow it.

This can be reproduced when the project depends on a package in a private registry and preinstall creates or refreshes the registry credentials:

  1. npm starts with a missing, expired, revoked, or deliberately invalid credential.
  2. The root preinstall script successfully obtains a valid credential and writes it to the user .npmrc.
  3. The same npm install still requests the private package using the configuration loaded before preinstall and fails with E401.
  4. Running npm install again succeeds because the new npm process loads the credential written by the first invocation.

The first invocation therefore fails even though authentication completed successfully before the private-registry request. This particularly affects private registries that use short-lived tokens and authentication helpers intended to refresh them automatically.

Expected Behavior

After the root preinstall hook completes, npm should reload the configuration or credentials used for subsequent dependency requests. A valid registry credential written by preinstall should be used by the active npm install, allowing the first invocation to succeed.

If reloading configuration after preinstall is intentionally unsupported, the npm 12 lifecycle documentation should make that limitation explicit. Running root preinstall before dependency installation otherwise appears to support authentication and configuration preparation, while the active installation silently retains its earlier state.

Steps To Reproduce

The following generic reproduction works with any private npm registry and avoids committing a real token. Back up any credentials before adapting it.

  1. Create a project containing a dependency from a private registry:

    {
      "name": "npm-preinstall-auth-repro",
      "version": "1.0.0",
      "private": true,
      "scripts": {
        "preinstall": "node refresh-auth.cjs"
      },
      "dependencies": {
        "@private/example": "1.0.0"
      }
    }
    
  2. Add the registry to the project .npmrc, replacing the example URL and scope:

    @private:registry=https://private-registry.example.test/npm/
    
  3. Save working credentials in an untracked file named valid-user.npmrc:

    //private-registry.example.test/npm/:_authToken=VALID_TOKEN
    
  4. Create refresh-auth.cjs. It replaces the user configuration during root preinstall:

    const { copyFileSync } = require("node:fs");
    const { join } = require("node:path");
    const { homedir } = require("node:os");
    
    const target =
      process.env.npm_config_userconfig ||
      process.env.NPM_CONFIG_USERCONFIG ||
      join(homedir(), ".npmrc");
    copyFileSync("valid-user.npmrc", target);
    console.log(`Wrote valid registry credentials to ${target}`);
    
  5. Point npm at a disposable user configuration containing an invalid token. In PowerShell:

    $env:NPM_CONFIG_USERCONFIG = "$PWD/repro-user.npmrc"
    Set-Content -LiteralPath $env:NPM_CONFIG_USERCONFIG -Value '//private-registry.example.test/npm/:_authToken=INVALID_TOKEN'
    
  6. Run the installation with lifecycle and request output visible:

    npm install --foreground-scripts --loglevel verbose
    
  7. Observe that refresh-auth.cjs reports that it wrote the valid credential, but the subsequent private-registry request receives 401 Unauthorized and npm exits with E401.

  8. Run the same command again without changing any files:

    npm install --foreground-scripts --loglevel verbose
    
  9. Observe that the second invocation succeeds because it loads repro-user.npmrc after the first invocation replaced its invalid credential.

The missing-credential variant behaves the same way: begin with a user configuration containing no registry credential, let preinstall add it, and observe that only the second npm invocation uses it.

Environment

The private registry hostname, username, and filesystem paths below are sanitized. No token value is included.

  • npm: 12.0.2

  • Node.js: v24.18.1

  • OS Name: Microsoft Windows 11 Enterprise 10.0.26200, 64-bit

  • System Model Name: Dell Pro Max 16 MC16250

  • npm config:

    ; "user" config from C:\Users\<user>\.npmrc
    
    //private-registry.example.test/npm/:_password = (protected)
    //private-registry.example.test/npm/:email = (protected)
    //private-registry.example.test/npm/:username = (protected)
    
    ; "env" config from environment
    
    cache = "Z:\\packages\\npm"
    
    ; node bin location = C:\nvm4w\nodejs\node.exe
    ; node version = v24.18.1
    ; npm local prefix = <project-path>
    ; npm version = 12.0.2
    ; cwd = <project-path>
    ; HOME = C:\Users\<user>
    ; Run `npm config ls -l` to show all defaults.
    

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with npm install's configuration loading and root preinstall lifecycle entry points, then reproduce the issue with the provided private-registry setup and verbose logging. Trace when the user and project .npmrc files are read relative to preinstall; done means the active install uses credentials written by preinstall, or the lifecycle documentation clearly states that it does not.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
cli, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
54/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.