@pnpm/network.web-auth 1101.1.2 → 1101.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/withOtpHandling.d.ts +16 -1
- package/lib/withOtpHandling.js +46 -7
- package/package.json +2 -2
package/lib/index.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ export { generateQrCode } from './generateQrCode.js';
|
|
|
2
2
|
export { pollForWebAuthToken, type PollForWebAuthTokenParams, type WebAuthContext, type WebAuthFetchOptions, type WebAuthFetchResponse, type WebAuthFetchResponseHeaders, } from './pollForWebAuthToken.js';
|
|
3
3
|
export { promptBrowserOpen, type PromptBrowserOpenContext, type PromptBrowserOpenParams, type PromptBrowserOpenReadlineInterface, } from './promptBrowserOpen.js';
|
|
4
4
|
export { WebAuthTimeoutError } from './WebAuthTimeoutError.js';
|
|
5
|
-
export { isOtpError, type OtpContext, type OtpEnquirer, type OtpHandlingParams, OtpNonInteractiveError, type OtpProcess, OtpSecondChallengeError, SyntheticOtpError, withOtpHandling, } from './withOtpHandling.js';
|
|
5
|
+
export { canonicalHttpUrl, isOtpError, type OtpContext, type OtpEnquirer, type OtpHandlingParams, OtpNonInteractiveError, type OtpProcess, OtpSecondChallengeError, SyntheticOtpError, withOtpHandling, } from './withOtpHandling.js';
|
package/lib/index.js
CHANGED
|
@@ -2,5 +2,5 @@ export { generateQrCode } from './generateQrCode.js';
|
|
|
2
2
|
export { pollForWebAuthToken, } from './pollForWebAuthToken.js';
|
|
3
3
|
export { promptBrowserOpen, } from './promptBrowserOpen.js';
|
|
4
4
|
export { WebAuthTimeoutError } from './WebAuthTimeoutError.js';
|
|
5
|
-
export { isOtpError, OtpNonInteractiveError, OtpSecondChallengeError, SyntheticOtpError, withOtpHandling, } from './withOtpHandling.js';
|
|
5
|
+
export { canonicalHttpUrl, isOtpError, OtpNonInteractiveError, OtpSecondChallengeError, SyntheticOtpError, withOtpHandling, } from './withOtpHandling.js';
|
|
6
6
|
//# sourceMappingURL=index.js.map
|
package/lib/withOtpHandling.d.ts
CHANGED
|
@@ -69,8 +69,23 @@ export declare class SyntheticOtpError extends Error implements OtpError {
|
|
|
69
69
|
static fromUnknownBody(globalWarn: OtpContext['globalWarn'], body: unknown): SyntheticOtpError;
|
|
70
70
|
}
|
|
71
71
|
export declare class OtpNonInteractiveError extends PnpmError {
|
|
72
|
-
|
|
72
|
+
readonly authUrl?: string;
|
|
73
|
+
readonly doneUrl?: string;
|
|
74
|
+
constructor(body?: OtpErrorBody);
|
|
73
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Returns the canonical serialization of an `http:`/`https:` URL with any
|
|
78
|
+
* userinfo (`user:pass@`) stripped, or `undefined` for a non-string, an
|
|
79
|
+
* unparsable URL, or any other scheme.
|
|
80
|
+
*
|
|
81
|
+
* These URLs come from the registry and get displayed, opened in a browser,
|
|
82
|
+
* and emitted in parseable error output: the scheme restriction keeps a
|
|
83
|
+
* malicious registry from injecting e.g. a `javascript:` URL into something
|
|
84
|
+
* that opens it, and stripping userinfo keeps credential-shaped data out of
|
|
85
|
+
* logs (the capability tokens automation needs live in the path/query, which
|
|
86
|
+
* are preserved).
|
|
87
|
+
*/
|
|
88
|
+
export declare function canonicalHttpUrl(value: unknown): string | undefined;
|
|
74
89
|
export declare class OtpSecondChallengeError extends PnpmError {
|
|
75
90
|
constructor();
|
|
76
91
|
}
|
package/lib/withOtpHandling.js
CHANGED
|
@@ -30,19 +30,21 @@ export async function withOtpHandling({ context, fetchOptions, operation, }) {
|
|
|
30
30
|
if (!isOtpError(error))
|
|
31
31
|
throw error;
|
|
32
32
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
33
|
-
throw new OtpNonInteractiveError();
|
|
33
|
+
throw new OtpNonInteractiveError(error.body);
|
|
34
34
|
}
|
|
35
35
|
let otp;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
const authUrl = canonicalHttpUrl(error.body?.authUrl);
|
|
37
|
+
const doneUrl = canonicalHttpUrl(error.body?.doneUrl);
|
|
38
|
+
if (authUrl != null && doneUrl != null) {
|
|
39
|
+
const qrCode = generateQrCode(authUrl);
|
|
40
|
+
globalInfo(`Authenticate your account at:\n${authUrl}\n\n${qrCode}`);
|
|
39
41
|
const pollPromise = pollForWebAuthToken({
|
|
40
42
|
context,
|
|
41
|
-
doneUrl
|
|
43
|
+
doneUrl,
|
|
42
44
|
fetchOptions,
|
|
43
45
|
});
|
|
44
46
|
otp = await promptBrowserOpen({
|
|
45
|
-
authUrl
|
|
47
|
+
authUrl,
|
|
46
48
|
context,
|
|
47
49
|
pollPromise,
|
|
48
50
|
});
|
|
@@ -113,10 +115,47 @@ export class SyntheticOtpError extends Error {
|
|
|
113
115
|
}
|
|
114
116
|
}
|
|
115
117
|
export class OtpNonInteractiveError extends PnpmError {
|
|
116
|
-
|
|
118
|
+
authUrl;
|
|
119
|
+
doneUrl;
|
|
120
|
+
constructor(body) {
|
|
117
121
|
super('OTP_NON_INTERACTIVE', 'The registry requires additional authentication, but pnpm is not running in an interactive terminal', {
|
|
118
122
|
hint: 'Re-run this command in an interactive terminal to complete authentication, or provide the --otp option if you are using a classic one-time password (OTP)',
|
|
119
123
|
});
|
|
124
|
+
const authUrl = canonicalHttpUrl(body?.authUrl);
|
|
125
|
+
if (authUrl != null) {
|
|
126
|
+
this.authUrl = authUrl;
|
|
127
|
+
}
|
|
128
|
+
const doneUrl = canonicalHttpUrl(body?.doneUrl);
|
|
129
|
+
if (doneUrl != null) {
|
|
130
|
+
this.doneUrl = doneUrl;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Returns the canonical serialization of an `http:`/`https:` URL with any
|
|
136
|
+
* userinfo (`user:pass@`) stripped, or `undefined` for a non-string, an
|
|
137
|
+
* unparsable URL, or any other scheme.
|
|
138
|
+
*
|
|
139
|
+
* These URLs come from the registry and get displayed, opened in a browser,
|
|
140
|
+
* and emitted in parseable error output: the scheme restriction keeps a
|
|
141
|
+
* malicious registry from injecting e.g. a `javascript:` URL into something
|
|
142
|
+
* that opens it, and stripping userinfo keeps credential-shaped data out of
|
|
143
|
+
* logs (the capability tokens automation needs live in the path/query, which
|
|
144
|
+
* are preserved).
|
|
145
|
+
*/
|
|
146
|
+
export function canonicalHttpUrl(value) {
|
|
147
|
+
if (typeof value !== 'string')
|
|
148
|
+
return undefined;
|
|
149
|
+
try {
|
|
150
|
+
const url = new URL(value);
|
|
151
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:')
|
|
152
|
+
return undefined;
|
|
153
|
+
url.username = '';
|
|
154
|
+
url.password = '';
|
|
155
|
+
return url.href;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return undefined;
|
|
120
159
|
}
|
|
121
160
|
}
|
|
122
161
|
export class OtpSecondChallengeError extends PnpmError {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/network.web-auth",
|
|
3
|
-
"version": "1101.
|
|
3
|
+
"version": "1101.2.0",
|
|
4
4
|
"description": "Web-based authentication flow with QR code display and token polling",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@jest/globals": "30.4.1",
|
|
37
37
|
"@types/qrcode-terminal": "^0.12.2",
|
|
38
|
-
"@pnpm/network.web-auth": "1101.
|
|
38
|
+
"@pnpm/network.web-auth": "1101.2.0"
|
|
39
39
|
},
|
|
40
40
|
"engines": {
|
|
41
41
|
"node": ">=22.13"
|