aspect-build / aspect-build/rules_js

[Bug]: npm_translate_lock doesn't use credential helper / tokenHelper for update_pnpm_lock

Closed
#3,004 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Starlark
Stars
378
Forks
183
Avg merge
1d 9h
Merged PRs (30d)
32

Description

### What happened?

### Problem

When `update_pnpm_lock = True`, running `pnpm install --lockfile-only` fails to authenticate with private registries that use `tokenHelper` in .npmrc, even though package downloads work fine.

### Current Behavior

`npm_translate_lock` handles authentication differently for package downloads vs lock file updates:

1. Package downloads (`npm_import`): aspect_rules_js parses .npmrc, executes tokenHelper, and passes credentials to Bazel's downloader. This works correctly.
2. Lock file updates (`_update_pnpm_lock`): aspect_rules_js runs pnpm install --lockfile-only directly without any .npmrc processing or tokenHelper execution. pnpm then fails with 401 Unauthorized.

Additionally, pnpm itself disallows tokenHelper in project-level .npmrc for security reasons, so even if the file is available, pnpm won't use it:

```
[ERROR] tokenHelper must not be configured in project-level .npmrc
```

### Expected Behavior

`npm_translate_lock` should handle authentication for `pnpm install --lockfile-only` the same way it does for package downloads - by parsing .npmrc, executing tokenHelper if configured, and making credentials available to pnpm (e.g., by writing a temporary .npmrc with _authToken populated).

### Workaround

We're using a preupdate script that:
1. Invokes our Bazel credential helper to get the auth token
2. Replaces the symlinked .npmrc with a regular file containing the original content plus _authToken

```javascript
// Invoke credential-helper, parse response, inject into .npmrc
const output = execSync(`${credentialHelper} get`, { input: JSON.stringify({ uri: REGISTRY_URI }), ... });
const { headers } = JSON.parse(output);
const token = headers.Authorization[0].replace("Bearer ", "");

// Replace symlink with regular file to avoid modifying source tree
if (lstatSync(".npmrc").isSymbolicLink()) {
unlinkSync(".npmrc");
}
writeFileSync(".npmrc", `${originalNpmrc}\n${REGISTRY_URL}:_authToken=${token}\n`);
```

### Suggested Solution

In _update_pnpm_lock, before executing pnpm:
1. Parse the .npmrc (already done elsewhere in aspect_rules_js)
2. If tokenHelper is configured for any registry, execute it and capture the token
3. Write a modified .npmrc in the external repository with _authToken populated
4. Then run pnpm install --lockfile-only

This would make update_pnpm_lock work seamlessly with private registries that use tokenHelper, matching the behavior of package downloads.

### Version

Development (host) and target OS/architectures:

Output of `bazel --version`: 9.2.0

Version of the Aspect rules, or other relevant rules from your
`WORKSPACE` or `MODULE.bazel` file:

```
bazel_dep(name = "aspect_rules_js", version = "3.4.1")
```

Language(s) and/or frameworks involved:

JavaScript

### How to reproduce

```shell

```

### Any other information?

_No response_

Contributor guide

Open the contributing guide

Research direction

Start at the _update_pnpm_lock entry point and compare its authentication flow with the existing .npmrc parsing used for package downloads. Reproduce the pnpm install --lockfile-only failure against a private registry using tokenHelper, then verify that the update succeeds with credentials available through a temporary .npmrc without modifying the source tree.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
build-system
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.