-
-
Notifications
You must be signed in to change notification settings - Fork 154
Description
What happened?
Description
When using use_home_npmrc = True with npm_translate_lock, authentication tokens from ~/.npmrc are read once during the module extension evaluation and baked into the generated npm_import_rule repository rules. When these tokens expire (e.g., AWS CodeArtifact tokens expire after 12 hours), Bazel continues using the stale cached tokens, resulting in 401 Unauthorized errors.
Expected Behavior
Similar to how rules_python handles .netrc credentials, authentication tokens should be read fresh from ~/.npmrc on every download attempt, not cached statically. This ensures that when tokens are refreshed in ~/.npmrc, Bazel automatically uses the new credentials without requiring cache invalidation.
Current Behavior
npm_translate_lockreads~/.npmrconce during module extension evaluation (extensions.bzl:124-132)- Auth tokens are extracted and stored in
npm_authdictionary - These static auth values are passed to
npm_import_ruleattributes - When tokens expire, the stale tokens remain in Bazel's repository cache
- Downloads fail with 401 Unauthorized until
bazel clean --expungeis run
Reproduction Steps
- Configure a private npm registry that uses short-lived tokens (e.g., AWS CodeArtifact with 12-hour tokens)
- Set up
~/.npmrcwith fresh auth token://registry.example.com/:_authToken=<TOKEN> - Configure
npm_translate_lockwithuse_home_npmrc = Truein MODULE.bazel:npm.npm_translate_lock( name = "npm", pnpm_lock = "pnpm-lock.yaml", npmrc = ".npmrc", use_home_npmrc = True, )
- Run
bazel build //...successfully - Wait for token to expire (or manually expire it)
- Refresh token in
~/.npmrcby running auth command - Run
bazel build //...again - Result: 401 Unauthorized errors despite having fresh token in
~/.npmrc:ERROR: An error occurred during the fetch of repository 'aspect_rules_js++npm+npm__package__1.0.0': Error in download: java.io.IOException: Error downloading [https://registry.example.com/package/-/package-1.0.0.tgz] to /path/to/cache/package.tgz: GET returned 401 Unauthorized
Workaround
Run bazel clean --expunge after refreshing tokens to clear the repository cache.
Root Cause Analysis
The issue occurs in npm/extensions.bzl:
if attr.use_home_npmrc:
home_directory = repo_utils.get_home_directory(module_ctx)
if home_directory:
home_npmrc_path = "{}/{}".format(home_directory, ".npmrc")
home_npmrc = parse_npmrc(module_ctx.read(home_npmrc_path)) # ← READ ONCE
(registries2, npm_auth2) = npm_translate_lock_helpers.get_npm_auth(home_npmrc, home_npmrc_path, module_ctx.os.environ)
registries.update(registries2)
npm_auth.update(npm_auth2) # ← STORED STATICALLYThe auth tokens are then passed to npm_import.bzl which uses them in rctx.download():
rctx.download(
output = _TARBALL_FILENAME,
url = download_url,
integrity = rctx.attr.integrity,
auth = auth, # ← USES STALE AUTH
canonical_id = download_url,
)Comparison with rules_python
rules_python correctly handles this by reading .netrc on every download (python/private/auth.bzl):
def get_auth(ctx, urls, ctx_attr = None):
"""Utility for retrieving netrc-based authentication parameters"""
# ...
if ctx_attr.netrc:
netrc = read_netrc(ctx, ctx_attr.netrc)
elif "NETRC" in ctx.os.environ:
netrc = read_netrc(ctx, ctx.getenv("NETRC"))
else:
netrc = read_user_netrc(ctx) # ← READ FRESH EVERY TIME
return use_netrc(netrc, urls, ctx_attr.auth_patterns)This get_auth() function is called for every rctx.download() call in whl_library.bzl:
result = rctx.download(
url = urls,
output = filename,
sha256 = rctx.attr.sha256,
auth = get_auth(rctx, urls), # ← FRESH AUTH EVERY TIME
)Proposed Solution
Implement a similar pattern to rules_python:
- Create an
npm_auth.bzlfile similar torules_python/python/private/auth.bzl - Implement
get_npm_auth()that reads~/.npmrcdynamically usingrctx.read()on every call - Modify
npm_import.bzlto call this function instead of using static auth attributes:rctx.download( output = _TARBALL_FILENAME, url = download_url, integrity = rctx.attr.integrity, auth = get_npm_auth(rctx, [download_url]), # ← READ FRESH canonical_id = download_url, )
- Remove the
npm_auth,npm_auth_basic,npm_auth_username,npm_auth_passwordattributes fromnpm_import_ruleas they would no longer be needed
This would ensure credentials are always fresh and match the behavior of standard HTTP tools.
Version
Development (host) and target OS/architectures:
Output of bazel --version:
bazel 8.3.0
Version of the Aspect rules, or other relevant rules from your
WORKSPACE or MODULE.bazel file:
2.6.0
Language(s) and/or frameworks involved:
How to reproduce
Any other information?
No response