Skip to content
This repository was archived by the owner on May 14, 2026. It is now read-only.
This repository was archived by the owner on May 14, 2026. It is now read-only.

Support npm-style auth tokens and scoped registries from .npmrc #250

Description

@zkochan

Problem

Pacquet can currently only fetch from public registries. Real-world projects — especially anything using GitHub Packages, corporate npm mirrors, or private scoped packages — rely on per-host bearer tokens and scoped-registry routing declared in `.npmrc`, so pacquet aborts on the first private dep.

This blocks the "ship pacquet that installs from a pnpm-generated lockfile" milestone for any project that isn't 100% public.

Scope

Three `.npmrc` constructs to parse + honour:

  1. `//[/]/:_authToken=` — per-host bearer token. pnpm picks the longest-prefix match against the request URL, which lets a single host carry per-path tokens (e.g. a monorepo CI).
  2. `@:registry=` — when resolving `@scope/foo`, route to `` instead of the default registry.
  3. `${VAR}` env-var substitution inside auth values. Nearly every real line looks like
    `//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}`, so without this the feature is unusable in CI.

Implementation sketch

  • Extend `NpmrcAuth::from_ini` in `crates/npmrc/src/npmrc_auth.rs` to capture two new collections (`auth_tokens: HashMap<String, String>`, `scoped_registries: HashMap<String, String>`).
  • Add `${VAR}` expansion pass over parsed values using `std::env::var`; leave unexpanded placeholders intact (pnpm does this so a commented-out CI token doesn't silently become an empty string).
  • Mirror the fields onto `Npmrc` and apply them in `NpmrcAuth::apply_to`.
  • Resolution side: when a package key starts with `@<scope>/`, resolve with `scoped_registries.get("@")` as the registry base; otherwise fall back to the default.
  • Fetch side: when building a `reqwest` request, find the longest auth-token prefix that the URL starts with and set `Authorization: Bearer `. Factor into a helper so every fetch path (tarball + packument) uses it.

Explicitly out of scope (follow-ups)

  • TLS config (`ca` / `cafile` / `cert` / `key` / `strict-ssl`)
  • Proxy settings (`proxy` / `https-proxy` / `no-proxy` / `http-proxy` / `local-address`)
  • Basic-auth (`_auth` / `_password` / `username` / `email`)
  • `PNPM_CONFIG_*` env-var overrides

Each of those is self-contained and deserves its own issue.

Test coverage target

  • Parsing: longest-prefix match, case-insensitive host match, `${VAR}` substitution with both set and unset vars, ignoring lines we don't model.
  • Apply: scoped registry supersedes default for scoped packages.
  • Integration via the registry-mock: authenticated fetch returns 200 when the token matches, and an unauthenticated fetch against the same host 401s — proves the header actually goes out.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions