feat: pnpm login (old)#11055
Closed
KSXGitHub wants to merge 47 commits into
Closed
Conversation
…-auth Extract generateQrCode() and pollForWebAuthToken() from releasing/commands into a new shared package so that both `pnpm publish` and the upcoming `pnpm login` can reuse the web-based authentication flow with QR code display and doneUrl polling. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Add `pnpm login` (and `pnpm adduser` alias) for authenticating with npm registries. The command: - Tries web-based login first (POST /-/v1/login), displaying a QR code and polling for the token using @pnpm/network.web-auth - Falls back to classic username/password/email login (PUT /-/user/ org.couchdb.user:<username>) when web login is not supported (404/405) - Saves the received auth token to the user's global rc file Also fixes a tsgo build issue in releasing/commands where OtpWebAuthFetchOptions was used as a local type alias but was only available as a re-exported name. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Switch from globalThis.fetch to fetchWithAgent from @pnpm/network.fetch so that pnpm login respects proxy settings (httpProxy/httpsProxy/noProxy), custom SSL certificates (ca/cert/key), strictSsl, and retry configuration. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
- Type LoginContext.fetch using WebAuthFetchOptions/WebAuthFetchResponse from @pnpm/network.web-auth, extended with text() and wider method - Replace regex-based URL construction with new URL() constructor - Remove redundant LoginFetchInit type https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
- Static DEFAULT_CONTEXT constant instead of createDefaultContext factory - context = DEFAULT_CONTEXT default parameter instead of context?: Partial - Destructure context in function signatures for natural calling - Use plain fetch from @pnpm/network.fetch (like SHARED_CONTEXT in publish) - Context contains only side-effect functions and modules, not config https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Remove LoginFetchOptions and LoginFetchResponse. Type LoginContext.fetch as typeof fetch from @pnpm/network.fetch directly, eliminating all casts. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Web login doesn't return a username, so just report the registry. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Add readSettings and writeSettings to LoginContext so tests need no filesystem side effects. Remove @pnpm/prepare devDependency. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Tests now construct their own TEST_CONTEXT with all no-op mocks, eliminating any reliance on real side-effectful functions. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Each test now uses a different registry and configDir to verify URL construction, config key generation, and save path are correct for non-default options. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
All mock functions in TEST_CONTEXT now throw on unexpected calls, ensuring tests fail loudly if the code makes unanticipated side effects. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Replace custom.registry.io and private.reg.co with example.com and example.org (RFC 2606 reserved) to prevent domain squatting risks. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Default globalInfo in TEST_CONTEXT now throws. Each test overrides it to capture messages and asserts the expected output. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Drop explicit `string` annotation so the parameter matches the `RequestInfo` type expected by the fetch signature. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Use mockResponse helper with `as any` cast to satisfy the Response type, and String(url) for RequestInfo-to-string conversion. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
…FetchOptions types Derive the fetch signature from actual call-site usage instead of coupling to the concrete @pnpm/network.fetch type. This lets test mocks return plain objects without casts. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
a34d83b to
cd51829
Compare
These files are created by setup.js during preinstall and should not be tracked. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Remove Otp-prefixed re-exports (OtpWebAuthFetchOptions, OtpWebAuthFetchResponse, OtpWebAuthTimeoutError) that only existed as backwards-compatibility shims. Update the test to import directly from @pnpm/network.web-auth. Restore the named OtpDate interface that was unnecessarily inlined. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Add dependency-injected unit tests covering: - WebAuthTimeoutError: properties, code, hint, message - generateQrCode: basic output and input differentiation - pollForWebAuthToken: happy path, fetch argument passing, Retry-After handling (valid, non-finite, null, sub-interval, capped to remaining timeout, timeout during retry wait), error recovery (fetch throws, non-ok response, json parse error, missing token, empty token, multiple consecutive errors), custom timeout, poll interval timing All tests use fake Date.now() and setTimeout — no real timers or side effects. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Replace `.catch((e: WebAuthTimeoutError) => e)` pattern with `rejects.toMatchObject()` to avoid `string | WebAuthTimeoutError` union type issue when accessing `.timeout` property. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
… to login - Create `withOtpHandling<T>()` in `@pnpm/network.web-auth` that wraps any operation with EOTP challenge detection, web auth flow, and classic OTP prompting. - Refactor `publishWithOtpHandling` to delegate to the shared function. - Add OTP handling to `pnpm login`'s classic (CouchDB) login flow: detects 401 + `www-authenticate: otp` header and retries with the OTP code (or web auth token) in the `npm-otp` header. - Remove overly strict `this: this` constraints from WebAuthFetchResponse interfaces to improve cross-package type compatibility. - Add 13 unit tests for `withOtpHandling` (classic + webauth flows). - Add 4 login OTP tests (classic OTP, webauth OTP, non-401, non-otp 401). https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Replace `m.includes(url)` with a regex that checks the URL is bounded by whitespace or string boundaries, addressing the CodeQL "incomplete URL substring sanitization" finding. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Replace manual `.some()` with Jest's `toContainEqual(expect.stringMatching(...))` for better error messages on failure. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Use toMatchObject and toEqual instead of separate per-property expects. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Remove dead re-exports of OtpHandlingPromptOptions and OtpHandlingPromptResponse from releasing/commands/src/publish/otp.ts. Add tests for: - LOGIN_MISSING_CREDENTIALS (empty username in classic login) - LOGIN_NO_TOKEN (registry returns success without token) - LOGIN_INVALID_RESPONSE (web login returns incomplete response) - isWebLoginNotSupported with 405 status code https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
…/writeIniFile Use the actual function names in the LoginContext interface instead of abstract names, matching the implementations they wrap. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
OtpNonInteractiveError, OtpSecondChallengeError, and OtpHandlingEnquirer were re-exported only for the test file, which can import them directly from @pnpm/network.web-auth. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
All consumers already import SHARED_CONTEXT directly from ./utils/shared-context.js, making this re-export dead code. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Extract named interfaces for the Date and enquirer members of LoginContext instead of inlining their types. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S
Claude Code Web didn't rename them thoroughly, so I had to do it myself
Why did Claude Code Web misaligned?
This was referenced Mar 26, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR implements the
pnpm logincommand for authenticating with npm registries. The implementation supports both modern web-based authentication (with QR code display) and falls back to classic username/password authentication for registries that don't support the web flow.Key Changes
New
@pnpm/auth.commandspackage: Implements the login command with support for:/-/v1/loginendpoint with QR code generation.npmrcconfiguration fileNew
@pnpm/network.web-authpackage: Extracted shared web authentication utilities:pollForWebAuthToken(): Polls a registry's "done" URL until authentication token is receivedgenerateQrCode(): Generates QR codes for authentication URLsWebAuthTimeoutError: Custom error for authentication timeout (5 minute default)Retry-Afterheaders during pollingRefactored OTP authentication: Updated
releasing/commands/src/publish/otp.tsto use the new shared@pnpm/network.web-authutilities, eliminating code duplicationIntegration: Added login command to pnpm CLI and removed it from the not-implemented commands list
Implementation Details
.npmrcfile under registry-specific keys (e.g.,//registry.npmjs.org/:_authToken)https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S